circle-ir 3.32.0 → 3.34.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.
@@ -9638,10 +9638,16 @@ var FRAMEWORK_MODULE_PATTERNS = [
9638
9638
  /^@nestjs\/core$/
9639
9639
  ];
9640
9640
  function extractRuntimeRegistrations(tree, cache, language, imports) {
9641
- if (language !== "javascript" && language !== "typescript") {
9642
- return [];
9641
+ if (language === "javascript" || language === "typescript") {
9642
+ return extractJSRuntimeRegistrations(tree, cache, imports);
9643
+ }
9644
+ if (language === "python") {
9645
+ return extractPythonRuntimeRegistrations(tree, cache, imports);
9646
+ }
9647
+ if (language === "rust") {
9648
+ return extractRustRuntimeRegistrations(tree, cache);
9643
9649
  }
9644
- return extractJSRuntimeRegistrations(tree, cache, imports);
9650
+ return [];
9645
9651
  }
9646
9652
  function buildHandlerIndex(tree, cache, imports) {
9647
9653
  const decls = /* @__PURE__ */ new Map();
@@ -9811,6 +9817,494 @@ function resolveHandler(node, index) {
9811
9817
  }
9812
9818
  return null;
9813
9819
  }
9820
+ var PY_HTTP_ROUTE_METHODS = /* @__PURE__ */ new Set([
9821
+ // Flask/Blueprint: app.route, blueprint.route, api.route
9822
+ "route",
9823
+ // FastAPI / Starlette / DRF method-specific
9824
+ "get",
9825
+ "post",
9826
+ "put",
9827
+ "patch",
9828
+ "delete",
9829
+ "head",
9830
+ "options"
9831
+ // Flask aliases (Flask 2.x): app.get/post/...
9832
+ ]);
9833
+ var PY_MIDDLEWARE_METHODS = /* @__PURE__ */ new Set([
9834
+ "before_request",
9835
+ "after_request",
9836
+ "teardown_request",
9837
+ "before_first_request",
9838
+ "teardown_appcontext",
9839
+ // Starlette / FastAPI
9840
+ "middleware"
9841
+ ]);
9842
+ var PY_EVENT_METHODS = /* @__PURE__ */ new Set([
9843
+ "errorhandler",
9844
+ "on_event",
9845
+ "exception_handler"
9846
+ // Celery beat etc — not strictly events but lifecycle
9847
+ ]);
9848
+ var PY_STDLIB_DECORATORS = /* @__PURE__ */ new Set([
9849
+ "property",
9850
+ "staticmethod",
9851
+ "classmethod",
9852
+ "abstractmethod",
9853
+ "cached_property",
9854
+ "dataclass",
9855
+ "cache",
9856
+ "lru_cache",
9857
+ "singledispatch",
9858
+ "singledispatchmethod",
9859
+ "contextmanager",
9860
+ "asynccontextmanager",
9861
+ "final",
9862
+ "override",
9863
+ "wraps"
9864
+ ]);
9865
+ function summarisePythonImports(imports) {
9866
+ const s = {
9867
+ hasFlask: false,
9868
+ hasFastApi: false,
9869
+ hasCelery: false,
9870
+ hasNumba: false,
9871
+ hasClick: false,
9872
+ hasPytest: false
9873
+ };
9874
+ if (!imports) return s;
9875
+ for (const imp of imports) {
9876
+ const mod = imp.from_package ?? "";
9877
+ if (!mod) continue;
9878
+ if (/^flask(\b|\.)/.test(mod)) s.hasFlask = true;
9879
+ if (/^fastapi(\b|\.)/.test(mod) || /^starlette(\b|\.)/.test(mod)) s.hasFastApi = true;
9880
+ if (/^celery(\b|\.)/.test(mod)) s.hasCelery = true;
9881
+ if (/^numba(\b|\.)/.test(mod)) s.hasNumba = true;
9882
+ if (/^click(\b|\.)/.test(mod)) s.hasClick = true;
9883
+ if (/^pytest(\b|\.)/.test(mod)) s.hasPytest = true;
9884
+ }
9885
+ return s;
9886
+ }
9887
+ function extractPythonRuntimeRegistrations(tree, cache, imports) {
9888
+ const out2 = [];
9889
+ const importSummary = summarisePythonImports(imports);
9890
+ const decoratedDefs = getNodesFromCache(tree.rootNode, "decorated_definition", cache);
9891
+ for (const dd of decoratedDefs) {
9892
+ let fnNode = null;
9893
+ const decorators = [];
9894
+ for (let i2 = 0; i2 < dd.childCount; i2++) {
9895
+ const child = dd.child(i2);
9896
+ if (!child) continue;
9897
+ if (child.type === "decorator") {
9898
+ decorators.push(child);
9899
+ } else if (child.type === "function_definition" || child.type === "async_function_definition") {
9900
+ fnNode = child;
9901
+ }
9902
+ }
9903
+ if (!fnNode || decorators.length === 0) continue;
9904
+ const handler = pythonHandlerFromFunctionDef(fnNode);
9905
+ if (!handler) continue;
9906
+ for (const dec of decorators) {
9907
+ const parsed = parsePythonDecorator(dec);
9908
+ if (!parsed) continue;
9909
+ const { receiver, method, path, line, column } = parsed;
9910
+ const { kind, framework } = classifyPythonDecorator(
9911
+ receiver,
9912
+ method,
9913
+ importSummary
9914
+ );
9915
+ out2.push({
9916
+ kind,
9917
+ framework,
9918
+ registrar: { method, receiver, line, column },
9919
+ ...path !== void 0 ? { path } : {},
9920
+ handler
9921
+ });
9922
+ }
9923
+ }
9924
+ return out2;
9925
+ }
9926
+ function pythonHandlerFromFunctionDef(fn) {
9927
+ const nameNode = fn.childForFieldName("name");
9928
+ if (!nameNode) return null;
9929
+ return {
9930
+ name: getNodeText(nameNode),
9931
+ line: fn.startPosition.row + 1,
9932
+ column: fn.startPosition.column
9933
+ };
9934
+ }
9935
+ function parsePythonDecorator(dec) {
9936
+ let target = null;
9937
+ for (let i2 = 0; i2 < dec.childCount; i2++) {
9938
+ const child = dec.child(i2);
9939
+ if (!child || child.type === "@") continue;
9940
+ target = child;
9941
+ break;
9942
+ }
9943
+ if (!target) return null;
9944
+ const line = dec.startPosition.row + 1;
9945
+ const column = dec.startPosition.column;
9946
+ if (target.type === "identifier") {
9947
+ return { receiver: "", method: getNodeText(target), line, column };
9948
+ }
9949
+ if (target.type === "attribute") {
9950
+ const { receiver, method } = splitDottedAttribute(target);
9951
+ return { receiver, method, line, column };
9952
+ }
9953
+ if (target.type === "call") {
9954
+ const fnNode = target.childForFieldName("function");
9955
+ if (!fnNode) return null;
9956
+ let receiver = "";
9957
+ let method = "";
9958
+ if (fnNode.type === "identifier") {
9959
+ method = getNodeText(fnNode);
9960
+ } else if (fnNode.type === "attribute") {
9961
+ const split = splitDottedAttribute(fnNode);
9962
+ receiver = split.receiver;
9963
+ method = split.method;
9964
+ } else {
9965
+ method = getNodeText(fnNode);
9966
+ }
9967
+ const path = extractFirstStringArg(target);
9968
+ return { receiver, method, path, line, column };
9969
+ }
9970
+ return null;
9971
+ }
9972
+ function splitDottedAttribute(attr) {
9973
+ const objectNode = attr.childForFieldName("object");
9974
+ const attrNode = attr.childForFieldName("attribute");
9975
+ const method = attrNode ? getNodeText(attrNode) : "";
9976
+ const receiver = objectNode ? getNodeText(objectNode) : "";
9977
+ return { receiver, method };
9978
+ }
9979
+ function extractFirstStringArg(call) {
9980
+ const argsNode = call.childForFieldName("arguments");
9981
+ if (!argsNode) return void 0;
9982
+ for (let i2 = 0; i2 < argsNode.childCount; i2++) {
9983
+ const child = argsNode.child(i2);
9984
+ if (!child) continue;
9985
+ if (child.type === "(" || child.type === ")" || child.type === ",") continue;
9986
+ if (child.type === "string") {
9987
+ return stripPythonStringQuotes(getNodeText(child));
9988
+ }
9989
+ return void 0;
9990
+ }
9991
+ return void 0;
9992
+ }
9993
+ function stripPythonStringQuotes(s) {
9994
+ const m = s.match(/^[bBrRuUfF]{0,2}(['"])(.*)\1$/s);
9995
+ if (m) return m[2];
9996
+ if (s.length >= 2 && (s[0] === '"' || s[0] === "'") && s[s.length - 1] === s[0]) {
9997
+ return s.slice(1, -1);
9998
+ }
9999
+ return s;
10000
+ }
10001
+ function classifyPythonDecorator(receiver, method, imp) {
10002
+ if (!receiver && PY_STDLIB_DECORATORS.has(method)) {
10003
+ return { kind: "decorator", framework: "stdlib" };
10004
+ }
10005
+ if (receiver) {
10006
+ const head = receiver.split(".")[0];
10007
+ if (head === "pytest") {
10008
+ return { kind: "decorator", framework: "pytest" };
10009
+ }
10010
+ if (head === "click") {
10011
+ return { kind: "decorator", framework: "click" };
10012
+ }
10013
+ if (head === "numba" || head === "nb") {
10014
+ return { kind: "decorator", framework: "numba" };
10015
+ }
10016
+ if (head === "celery") {
10017
+ return { kind: "decorator", framework: "celery" };
10018
+ }
10019
+ }
10020
+ if (receiver && PY_HTTP_ROUTE_METHODS.has(method)) {
10021
+ const isRoutey = isPyRouterReceiver(receiver);
10022
+ if (isRoutey) {
10023
+ let framework = "unknown";
10024
+ if (imp.hasFlask) framework = "flask";
10025
+ else if (imp.hasFastApi) framework = "fastapi";
10026
+ else if (method === "route") framework = "flask";
10027
+ else framework = "fastapi";
10028
+ return { kind: "http_route", framework };
10029
+ }
10030
+ }
10031
+ if (receiver && PY_MIDDLEWARE_METHODS.has(method)) {
10032
+ return { kind: "middleware", framework: imp.hasFlask ? "flask" : imp.hasFastApi ? "fastapi" : "unknown" };
10033
+ }
10034
+ if (receiver && PY_EVENT_METHODS.has(method)) {
10035
+ return { kind: "event_listener", framework: imp.hasFlask ? "flask" : imp.hasFastApi ? "fastapi" : "unknown" };
10036
+ }
10037
+ if (method === "task" && imp.hasCelery) {
10038
+ return { kind: "decorator", framework: "celery" };
10039
+ }
10040
+ if (!receiver && (method === "login_required" || method === "require_http_methods" || method === "api_view")) {
10041
+ return { kind: "decorator", framework: "django" };
10042
+ }
10043
+ return { kind: "decorator", framework: "unknown" };
10044
+ }
10045
+ function isPyRouterReceiver(receiver) {
10046
+ const head = receiver.split(".")[0];
10047
+ if (!head) return false;
10048
+ if (["app", "router", "blueprint", "bp", "api", "application"].includes(head)) return true;
10049
+ if (/_(router|bp|blueprint|app|api)$/.test(head)) return true;
10050
+ return false;
10051
+ }
10052
+ var RUST_STDLIB_TRAITS = /* @__PURE__ */ new Set([
10053
+ // Formatting
10054
+ "Display",
10055
+ "Debug",
10056
+ "Write",
10057
+ // Conversion
10058
+ "From",
10059
+ "Into",
10060
+ "TryFrom",
10061
+ "TryInto",
10062
+ "AsRef",
10063
+ "AsMut",
10064
+ "ToString",
10065
+ "FromStr",
10066
+ // Iteration
10067
+ "Iterator",
10068
+ "IntoIterator",
10069
+ "FromIterator",
10070
+ "DoubleEndedIterator",
10071
+ "ExactSizeIterator",
10072
+ "FusedIterator",
10073
+ // Comparison + hashing
10074
+ "PartialEq",
10075
+ "Eq",
10076
+ "PartialOrd",
10077
+ "Ord",
10078
+ "Hash",
10079
+ // Markers + defaults
10080
+ "Default",
10081
+ "Copy",
10082
+ "Clone",
10083
+ "Send",
10084
+ "Sync",
10085
+ "Unpin",
10086
+ "Sized",
10087
+ "Any",
10088
+ // Resource management
10089
+ "Drop",
10090
+ // Async
10091
+ "Future",
10092
+ "IntoFuture",
10093
+ // Operators
10094
+ "Add",
10095
+ "Sub",
10096
+ "Mul",
10097
+ "Div",
10098
+ "Rem",
10099
+ "Neg",
10100
+ "Not",
10101
+ "AddAssign",
10102
+ "SubAssign",
10103
+ "MulAssign",
10104
+ "DivAssign",
10105
+ "RemAssign",
10106
+ "BitAnd",
10107
+ "BitOr",
10108
+ "BitXor",
10109
+ "Shl",
10110
+ "Shr",
10111
+ "Deref",
10112
+ "DerefMut",
10113
+ "Index",
10114
+ "IndexMut",
10115
+ // Closures
10116
+ "Fn",
10117
+ "FnMut",
10118
+ "FnOnce",
10119
+ // Error + I/O
10120
+ "Error",
10121
+ "Read",
10122
+ "Write",
10123
+ "Seek",
10124
+ "BufRead",
10125
+ // Misc
10126
+ "Borrow",
10127
+ "BorrowMut",
10128
+ "ToOwned"
10129
+ ]);
10130
+ var RUST_TRAIT_FRAMEWORK_PREFIXES = [
10131
+ { prefix: /^actix(_web)?(::|$)/, framework: "actix" },
10132
+ { prefix: /^axum(::|$)/, framework: "axum" },
10133
+ { prefix: /^rocket(::|$)/, framework: "rocket" },
10134
+ { prefix: /^tokio(::|$)/, framework: "tokio" },
10135
+ { prefix: /^serde(_\w+)?(::|$)/, framework: "serde" },
10136
+ { prefix: /^std(::|$)/, framework: "stdlib" },
10137
+ { prefix: /^core(::|$)/, framework: "stdlib" },
10138
+ { prefix: /^alloc(::|$)/, framework: "stdlib" }
10139
+ ];
10140
+ function extractRustRuntimeRegistrations(tree, cache) {
10141
+ const regs = [];
10142
+ const implNodes = getNodesFromCache(tree.rootNode, "impl_item", cache);
10143
+ for (const impl of implNodes) {
10144
+ collectRustImplRegistrations(impl, regs);
10145
+ }
10146
+ const macroNodes = getNodesFromCache(tree.rootNode, "macro_invocation", cache);
10147
+ for (const macro of macroNodes) {
10148
+ const rec = parseInventorySubmit(macro);
10149
+ if (rec) regs.push(rec);
10150
+ }
10151
+ const attrNodes = getNodesFromCache(tree.rootNode, "attribute_item", cache);
10152
+ for (const attr of attrNodes) {
10153
+ const rec = parseDistributedSliceAttribute(attr);
10154
+ if (rec) regs.push(rec);
10155
+ }
10156
+ return regs;
10157
+ }
10158
+ function collectRustImplRegistrations(impl, regs) {
10159
+ const traitNode = impl.childForFieldName("trait");
10160
+ if (!traitNode) return;
10161
+ const typeNode = impl.childForFieldName("type");
10162
+ if (!typeNode) return;
10163
+ const traitText = getNodeText(traitNode).trim();
10164
+ const traitLastSegment = lastRustPathSegment(stripRustGenerics(traitText));
10165
+ const selfType = getNodeText(typeNode).trim();
10166
+ const framework = classifyRustTrait(traitText);
10167
+ const body2 = impl.childForFieldName("body");
10168
+ if (!body2) return;
10169
+ for (let i2 = 0; i2 < body2.childCount; i2++) {
10170
+ const child = body2.child(i2);
10171
+ if (!child || child.type !== "function_item") continue;
10172
+ const nameNode = child.childForFieldName("name");
10173
+ if (!nameNode) continue;
10174
+ const methodName = getNodeText(nameNode);
10175
+ regs.push({
10176
+ kind: "trait_impl",
10177
+ framework,
10178
+ registrar: {
10179
+ method: methodName,
10180
+ receiver: selfType,
10181
+ line: impl.startPosition.row + 1,
10182
+ column: impl.startPosition.column
10183
+ },
10184
+ path: traitLastSegment || traitText,
10185
+ handler: {
10186
+ name: methodName,
10187
+ line: child.startPosition.row + 1,
10188
+ column: child.startPosition.column
10189
+ }
10190
+ });
10191
+ }
10192
+ }
10193
+ function stripRustGenerics(text) {
10194
+ const idx = text.indexOf("<");
10195
+ return idx >= 0 ? text.slice(0, idx) : text;
10196
+ }
10197
+ function lastRustPathSegment(path) {
10198
+ const parts2 = path.split("::");
10199
+ return parts2[parts2.length - 1] || path;
10200
+ }
10201
+ function classifyRustTrait(traitText) {
10202
+ const stripped = stripRustGenerics(traitText).trim();
10203
+ const last = lastRustPathSegment(stripped);
10204
+ if (RUST_STDLIB_TRAITS.has(last)) return "stdlib";
10205
+ for (const { prefix, framework } of RUST_TRAIT_FRAMEWORK_PREFIXES) {
10206
+ if (prefix.test(stripped)) return framework;
10207
+ }
10208
+ return "unknown";
10209
+ }
10210
+ function parseInventorySubmit(macro) {
10211
+ const macroName = macro.childForFieldName("macro");
10212
+ if (!macroName) return null;
10213
+ const name2 = getNodeText(macroName).trim();
10214
+ if (name2 !== "inventory::submit" && name2 !== "submit") return null;
10215
+ if (name2 === "submit") return null;
10216
+ let tokenTree = null;
10217
+ for (let i2 = 0; i2 < macro.childCount; i2++) {
10218
+ const c = macro.child(i2);
10219
+ if (c && c.type === "token_tree") {
10220
+ tokenTree = c;
10221
+ break;
10222
+ }
10223
+ }
10224
+ if (!tokenTree) return null;
10225
+ const handlerName = firstIdentifierInTokenTree(tokenTree);
10226
+ return {
10227
+ kind: "trait_impl",
10228
+ framework: "inventory",
10229
+ registrar: {
10230
+ method: "submit",
10231
+ receiver: "inventory",
10232
+ line: macro.startPosition.row + 1,
10233
+ column: macro.startPosition.column
10234
+ },
10235
+ path: "inventory::submit",
10236
+ handler: {
10237
+ name: handlerName,
10238
+ line: tokenTree.startPosition.row + 1,
10239
+ column: tokenTree.startPosition.column
10240
+ }
10241
+ };
10242
+ }
10243
+ function firstIdentifierInTokenTree(tokenTree) {
10244
+ for (let i2 = 0; i2 < tokenTree.childCount; i2++) {
10245
+ const c = tokenTree.child(i2);
10246
+ if (!c) continue;
10247
+ if (c.type === "identifier" || c.type === "scoped_identifier" || c.type === "type_identifier") {
10248
+ return getNodeText(c).trim();
10249
+ }
10250
+ }
10251
+ return null;
10252
+ }
10253
+ function parseDistributedSliceAttribute(attrItem) {
10254
+ let attr = null;
10255
+ for (let i2 = 0; i2 < attrItem.childCount; i2++) {
10256
+ const c = attrItem.child(i2);
10257
+ if (c && c.type === "attribute") {
10258
+ attr = c;
10259
+ break;
10260
+ }
10261
+ }
10262
+ if (!attr) return null;
10263
+ const pathNode = attr.child(0);
10264
+ if (!pathNode) return null;
10265
+ const pathText = getNodeText(pathNode).trim();
10266
+ if (pathText !== "linkme::distributed_slice" && pathText !== "distributed_slice") return null;
10267
+ const parent = attrItem.parent;
10268
+ if (!parent) return null;
10269
+ let attrIndex = -1;
10270
+ for (let i2 = 0; i2 < parent.childCount; i2++) {
10271
+ const c = parent.child(i2);
10272
+ if (c && c.id === attrItem.id) {
10273
+ attrIndex = i2;
10274
+ break;
10275
+ }
10276
+ }
10277
+ if (attrIndex < 0) return null;
10278
+ let handlerNode = null;
10279
+ for (let j = attrIndex + 1; j < parent.childCount; j++) {
10280
+ const sib = parent.child(j);
10281
+ if (!sib) continue;
10282
+ if (sib.type === "attribute_item") continue;
10283
+ if (sib.type === "static_item" || sib.type === "function_item") {
10284
+ handlerNode = sib;
10285
+ }
10286
+ break;
10287
+ }
10288
+ if (!handlerNode) return null;
10289
+ const nameNode = handlerNode.childForFieldName("name");
10290
+ const handlerName = nameNode ? getNodeText(nameNode).trim() : null;
10291
+ return {
10292
+ kind: "trait_impl",
10293
+ framework: "linkme",
10294
+ registrar: {
10295
+ method: "distributed_slice",
10296
+ receiver: "linkme",
10297
+ line: attrItem.startPosition.row + 1,
10298
+ column: attrItem.startPosition.column
10299
+ },
10300
+ path: "linkme::distributed_slice",
10301
+ handler: {
10302
+ name: handlerName,
10303
+ line: handlerNode.startPosition.row + 1,
10304
+ column: handlerNode.startPosition.column
10305
+ }
10306
+ };
10307
+ }
9814
10308
 
9815
10309
  // src/analysis/config-loader.ts
9816
10310
  var DEFAULT_SOURCES = [
@@ -26316,7 +26810,9 @@ function getNodeTypesForLanguage(language) {
26316
26810
  "use_declaration",
26317
26811
  "let_declaration",
26318
26812
  "field_expression",
26319
- "scoped_identifier"
26813
+ "scoped_identifier",
26814
+ "attribute_item",
26815
+ "static_item"
26320
26816
  ]);
26321
26817
  case "python":
26322
26818
  return /* @__PURE__ */ new Set([
@@ -26327,7 +26823,9 @@ function getNodeTypesForLanguage(language) {
26327
26823
  "import_from_statement",
26328
26824
  "assignment",
26329
26825
  "attribute",
26330
- "subscript"
26826
+ "subscript",
26827
+ "decorated_definition",
26828
+ "decorator"
26331
26829
  ]);
26332
26830
  case "javascript":
26333
26831
  case "typescript":
@@ -1,25 +1,43 @@
1
1
  /**
2
- * Runtime-registration extractor (issue #15 — Phase 1).
2
+ * Runtime-registration extractor (issue #15 — Phases 1, 2, 3).
3
3
  *
4
- * Recognises JS/TS framework registration patterns where a handler is wired
5
- * into a dispatch table at module-load time. Static call extraction sees the
6
- * registration call (`app.get(...)`) but not the edge from registrar → handler.
4
+ * Recognises framework registration patterns where a handler is wired into a
5
+ * dispatch table at module-load time. Static call extraction sees the
6
+ * registration call/decorator but not the edge from registrar → handler.
7
7
  *
8
8
  * Downstream consumers (e.g. dead-code reachability) read
9
9
  * `ir.runtime_registrations` and add each resolved handler as a virtual entry
10
10
  * root, eliminating "unreachable" false positives for framework handlers.
11
11
  *
12
- * Phase 1 covers:
13
- * - Express-family HTTP routes: `app.METHOD(path?, ...handlers)`
14
- * where METHOD ∈ HTTP_VERBS and receiver is express-shaped
12
+ * Phase 1 — JS/TS Express-family (shipped 3.32.0):
13
+ * - HTTP routes: `app.METHOD(path?, ...handlers)` for METHOD ∈ HTTP_VERBS
15
14
  * - Middleware: `app.use(...handlers)`
16
- * - Event listeners: `emitter.on('event', handler)` (when receiver is
17
- * express-shaped, otherwise skipped to avoid false-positive registrations)
15
+ * - Event listeners: `emitter.on('event', handler)` on express-shaped receivers
18
16
  *
19
- * Out of scope for Phase 1:
20
- * - NestJS / Python decorators (Phase 2)
21
- * - Rust trait dispatch (Phase 3)
22
- * - Subapp mounting (`app.use('/api', subApp)`) handler resolution
17
+ * Phase 2 Python decorators (3.33.0):
18
+ * - Every `@decorator` on a function emits a registration with handler =
19
+ * decorated function. Known frameworks are tagged (flask, fastapi,
20
+ * django, click, pytest, celery, numba); built-in (property,
21
+ * staticmethod, etc.) is tagged `stdlib`. Routing-style decorators
22
+ * (`@app.route`, `@app.get`, `@router.post`) are classified as
23
+ * `kind: 'http_route'` so downstream consumers can treat JS routes and
24
+ * Python routes uniformly.
25
+ *
26
+ * Phase 3 — Rust trait dispatch (3.34.0):
27
+ * - `impl Trait for Type { fn method(...) }` emits one `trait_impl`
28
+ * registration per method, recording the Self type as `receiver`, the
29
+ * trait path as `path`, and the method as both `registrar.method` and
30
+ * `handler.name`. Stdlib traits (Display, Debug, Iterator, …) are tagged
31
+ * `framework: 'stdlib'`; known web/async/serde frameworks (actix, axum,
32
+ * rocket, tokio, serde) are tagged accordingly.
33
+ * - `inventory::submit! { … }` and `#[linkme::distributed_slice]` emit
34
+ * `trait_impl` registrations with framework `'inventory'` / `'linkme'`.
35
+ *
36
+ * Out of scope:
37
+ * - Subapp mounting (`app.use('/api', subApp)`) handler resolution.
38
+ * - Cross-file trait → impl resolution scoped by `Cargo.toml` reachability
39
+ * (file-local impls only at extraction time; project-level resolution is
40
+ * deferred to a later cross-file pass).
23
41
  */
24
42
  import type { Tree } from 'web-tree-sitter';
25
43
  import type { RuntimeRegistration, ImportInfo } from '../../types/index.js';
@@ -28,7 +46,9 @@ import type { SupportedLanguage } from '../parser.js';
28
46
  /**
29
47
  * Extract runtime-registration patterns from a parsed file.
30
48
  *
31
- * Returns `[]` for any language other than JavaScript/TypeScript in Phase 1.
49
+ * Phase 1 covers JavaScript/TypeScript. Phase 2 adds Python decorators.
50
+ * Phase 3 adds Rust trait dispatch (`impl Trait for Type`, `inventory::submit!`,
51
+ * `#[linkme::distributed_slice]`). Returns `[]` for any other language.
32
52
  */
33
53
  export declare function extractRuntimeRegistrations(tree: Tree, cache: NodeCache | undefined, language: SupportedLanguage | string, imports?: ImportInfo[]): RuntimeRegistration[];
34
54
  //# sourceMappingURL=runtime-registrations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-registrations.d.ts","sourceRoot":"","sources":["../../../src/core/extractors/runtime-registrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAQ,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AA2BtD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,QAAQ,EAAE,iBAAiB,GAAG,MAAM,EACpC,OAAO,CAAC,EAAE,UAAU,EAAE,GACrB,mBAAmB,EAAE,CAKvB"}
1
+ {"version":3,"file":"runtime-registrations.d.ts","sourceRoot":"","sources":["../../../src/core/extractors/runtime-registrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAQ,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AA2BtD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,QAAQ,EAAE,iBAAiB,GAAG,MAAM,EACpC,OAAO,CAAC,EAAE,UAAU,EAAE,GACrB,mBAAmB,EAAE,CAWvB"}