@happyvertical/smrt-scanner 0.46.0 → 0.47.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/AGENTS.md CHANGED
@@ -214,6 +214,21 @@ place, `toKnowledgeAgentSurface` in `vite-plugin/index.ts`.
214
214
  printed by the Vite plugin, carried into `smrt-knowledge.json`, reported by
215
215
  `smrt doctor`, and surfaced by `dev:knowledge-check` as
216
216
  `agent-surface-not-static`.
217
+ - **A parameter carries type PROVENANCE, a field does not.**
218
+ `RawParameterDefinition.typeUnresolved` marks an annotation the scanner could
219
+ not express (intersection, tuple, conditional, mapped, `typeof`, indexed
220
+ access) — `type` still reads `'any'` for compatibility, so that flag is the
221
+ only thing separating it from an authored `any`. `memberTypes` carries the
222
+ members of an INLINE object literal, which `extractTypeName` otherwise
223
+ flattens to `'object'`; NAMED bags stay unexpanded on purpose. Core's API
224
+ wire-ability gate fails closed on both (#2686). The bare `object` keyword is
225
+ resolved for parameters only — `ManifestAdapter.inferFromAnnotation` gives an
226
+ `object`-typed FIELD a `{}` column default, so naming it in `extractTypeName`
227
+ would silently change DDL.
228
+ - **`@method()` config is read with `requireLiteralValues`**, like `@smrt()`: an
229
+ unresolvable value is a scan error, because a dropped `expose: false` restores
230
+ the routing the author was withholding. `decoratorConfig` is `undefined` for
231
+ an undecorated method and `{}` for a bare `@method()`; the two differ.
217
232
  - **Relationship targets are resolved, not copied**: `@foreignKey`,
218
233
  `@oneToMany` and `@manyToMany` arguments arrive as raw source text.
219
234
  `'Target'`/`Target` pass through and a forward-reference thunk
@@ -1080,7 +1080,8 @@ function parseFile(filePath) {
1080
1080
  smrtImports = extractSmrtImports(program.body);
1081
1081
  const ctx = {
1082
1082
  constants: extractModuleObjectConstants(program.body, sourceText),
1083
- unresolved: []
1083
+ unresolved: [],
1084
+ importAliases
1084
1085
  };
1085
1086
  for (const node of program.body) {
1086
1087
  const extracted = extractClassFromNode(node, filePath, sourceText, importAliases, ctx);
@@ -1164,7 +1165,8 @@ function parseSource(sourceText, filename = "test.ts") {
1164
1165
  smrtImports = extractSmrtImports(program.body);
1165
1166
  const ctx = {
1166
1167
  constants: extractModuleObjectConstants(program.body, sourceText),
1167
- unresolved: []
1168
+ unresolved: [],
1169
+ importAliases
1168
1170
  };
1169
1171
  for (const node of program.body) {
1170
1172
  const extracted = extractClassFromNode(node, filename, sourceText, importAliases, ctx);
@@ -1491,7 +1493,7 @@ function reportUnresolvedSpreads(unresolved, filePath, sourceText, errors) {
1491
1493
  for (const spread of unresolved) {
1492
1494
  const loc = spread.start === void 0 ? void 0 : getLineColumn(sourceText, spread.start);
1493
1495
  errors.push({
1494
- message: `Cannot statically resolve \`${spread.expression}\` while expanding a @smrt() config. Only literal keys/values and unescaped module-scope \`const\` object literals in the same file are supported. Inline the keys or remove the mutation/alias \u2014 an unresolved expression would drop api/mcp/cli from the manifest, and an absent surface defaults to open.`,
1496
+ message: `Cannot statically resolve \`${spread.expression}\` while expanding a ${spread.decorator ?? "@smrt()"} config. Only literal keys/values and unescaped module-scope \`const\` object literals in the same file are supported. Inline the keys or remove the mutation/alias \u2014 an unresolved expression would drop keys from the manifest, and every absent exposure key defaults to open.`,
1495
1497
  filePath,
1496
1498
  line: loc?.line,
1497
1499
  column: loc?.column,
@@ -1508,9 +1510,9 @@ function extractClassFromNode(node, filePath, sourceText, importAliases, ctx) {
1508
1510
  function extractClassDeclaration(node, filePath, sourceText, importAliases, ctx) {
1509
1511
  const className = node.id?.name || "AnonymousClass";
1510
1512
  const decorators = node.decorators || [];
1511
- const smrtDecorator = decorators.find((d) => isSmrtDecorator(d));
1512
- const reportDecorator = decorators.find((d) => isNamedDecorator(d, "report"));
1513
- const tenantScopedDecorator = decorators.find((d) => isNamedDecorator(d, "TenantScoped"));
1513
+ const smrtDecorator = decorators.find((d) => isSmrtDecorator(d, importAliases));
1514
+ const reportDecorator = decorators.find((d) => isNamedDecorator(d, "report", importAliases));
1515
+ const tenantScopedDecorator = decorators.find((d) => isNamedDecorator(d, "TenantScoped", importAliases));
1514
1516
  const hasSmartDecorator = !!smrtDecorator;
1515
1517
  const smrtConfig = smrtDecorator ? extractDecoratorConfig(smrtDecorator, sourceText, ctx ? {
1516
1518
  ...ctx,
@@ -1528,7 +1530,7 @@ function extractClassDeclaration(node, filePath, sourceText, importAliases, ctx)
1528
1530
  const field = extractPropertyDefinition(member, sourceText);
1529
1531
  if (field) fields.push(field);
1530
1532
  } else if (member.type === "MethodDefinition") {
1531
- const method = extractMethodDefinition(member, sourceText);
1533
+ const method = extractMethodDefinition(member, sourceText, ctx);
1532
1534
  if (method) methods.push(method);
1533
1535
  }
1534
1536
  return {
@@ -1544,16 +1546,17 @@ function extractClassDeclaration(node, filePath, sourceText, importAliases, ctx)
1544
1546
  endLine: node.loc?.end.line || 1
1545
1547
  };
1546
1548
  }
1547
- function isSmrtDecorator(decorator) {
1548
- return isNamedDecorator(decorator, "smrt");
1549
+ function isSmrtDecorator(decorator, importAliases) {
1550
+ return isNamedDecorator(decorator, "smrt", importAliases);
1549
1551
  }
1550
- function isNamedDecorator(decorator, name) {
1552
+ function isNamedDecorator(decorator, name, importAliases) {
1551
1553
  const expr = decorator.expression;
1554
+ const resolve = (local) => importAliases?.get(local) ?? local;
1552
1555
  if (expr.type === "CallExpression") {
1553
1556
  const callee = expr.callee;
1554
- if (callee.type === "Identifier" && callee.name === name) return true;
1557
+ if (callee.type === "Identifier" && resolve(callee.name) === name) return true;
1555
1558
  }
1556
- if (expr.type === "Identifier" && expr.name === name) return true;
1559
+ if (expr.type === "Identifier" && resolve(expr.name) === name) return true;
1557
1560
  return false;
1558
1561
  }
1559
1562
  function extractDecoratorConfig(decorator, sourceText, ctx) {
@@ -1757,7 +1760,8 @@ function extractTypeName(type) {
1757
1760
  else if (type.typeName.type === "TSQualifiedName") baseName = getQualifiedName(type.typeName);
1758
1761
  const typeParams = type.typeArguments?.params || type.typeParameters?.params;
1759
1762
  if (baseName && typeParams?.length) {
1760
- const typeArgs = typeParams.map((p) => extractTypeName(p)).filter(Boolean);
1763
+ const typeArgs = typeParams.map((p) => extractTypeName(p));
1764
+ if (typeArgs.some((arg) => arg === null)) return null;
1761
1765
  if (typeArgs.length > 0) return `${baseName}<${typeArgs.join(", ")}>`;
1762
1766
  }
1763
1767
  return baseName;
@@ -1766,7 +1770,12 @@ function extractTypeName(type) {
1766
1770
  case "TSNumberKeyword": return "number";
1767
1771
  case "TSBooleanKeyword": return "boolean";
1768
1772
  case "TSAnyKeyword": return "any";
1773
+ case "TSUnknownKeyword": return "unknown";
1774
+ case "TSNeverKeyword": return "never";
1775
+ case "TSBigIntKeyword": return "bigint";
1776
+ case "TSSymbolKeyword": return "symbol";
1769
1777
  case "TSVoidKeyword": return "void";
1778
+ case "TSThisType": return "this";
1770
1779
  case "TSNullKeyword": return "null";
1771
1780
  case "TSUndefinedKeyword": return "undefined";
1772
1781
  case "TSLiteralType": {
@@ -1781,7 +1790,11 @@ function extractTypeName(type) {
1781
1790
  const elementType = extractTypeName(type.elementType);
1782
1791
  return elementType ? `${elementType}[]` : null;
1783
1792
  }
1784
- case "TSUnionType": return type.types.map((t) => extractTypeName(t)).filter(Boolean).join(" | ");
1793
+ case "TSUnionType": {
1794
+ const types = type.types.map((t) => extractTypeName(t));
1795
+ if (types.some((branch) => branch === null)) return null;
1796
+ return types.join(" | ");
1797
+ }
1785
1798
  case "TSTypeLiteral": return "object";
1786
1799
  case "TSFunctionType": return "Function";
1787
1800
  default: return null;
@@ -1861,7 +1874,7 @@ function extractFieldDecorator(decorator, sourceText) {
1861
1874
  arguments: args
1862
1875
  };
1863
1876
  }
1864
- function extractMethodDefinition(node, sourceText) {
1877
+ function extractMethodDefinition(node, sourceText, ctx) {
1865
1878
  if (node.kind !== "method") return null;
1866
1879
  const name = getPropertyKey(node.key);
1867
1880
  if (!name) return null;
@@ -1872,6 +1885,7 @@ function extractMethodDefinition(node, sourceText) {
1872
1885
  if (extracted) parameters.push(extracted);
1873
1886
  }
1874
1887
  const returnType = func.returnType ? extractTypeName(func.returnType.typeAnnotation) : null;
1888
+ const decoratorConfig = extractMethodDecoratorConfig(node, sourceText, ctx);
1875
1889
  return {
1876
1890
  name,
1877
1891
  async: func.async,
@@ -1880,21 +1894,113 @@ function extractMethodDefinition(node, sourceText) {
1880
1894
  parameters,
1881
1895
  returnType,
1882
1896
  description: null,
1897
+ ...decoratorConfig ? { decoratorConfig } : {},
1883
1898
  line: node.loc?.start.line || 0
1884
1899
  };
1885
1900
  }
1901
+ function extractMethodDecoratorConfig(node, sourceText, ctx) {
1902
+ const decorator = node.decorators?.find((d) => isNamedDecorator(d, "method", ctx?.importAliases));
1903
+ if (!decorator) return void 0;
1904
+ const unresolvedBefore = ctx?.unresolved.length ?? 0;
1905
+ const config = extractDecoratorConfig(decorator, sourceText, ctx ? {
1906
+ ...ctx,
1907
+ requireLiteralValues: true
1908
+ } : ctx);
1909
+ if (ctx) for (const entry of ctx.unresolved.slice(unresolvedBefore)) entry.decorator = "@method()";
1910
+ return config ?? {};
1911
+ }
1912
+ var INLINE_MEMBER_TYPE_MAX_DEPTH = 4;
1913
+ function describeParameterType(annotation) {
1914
+ if (!annotation) return {
1915
+ type: null,
1916
+ typeUnresolved: false
1917
+ };
1918
+ const node = annotation.typeAnnotation;
1919
+ if (node.type === "TSObjectKeyword") return {
1920
+ type: "object",
1921
+ typeUnresolved: false
1922
+ };
1923
+ const type = extractTypeName(node);
1924
+ if (type === null) return {
1925
+ type: null,
1926
+ typeUnresolved: true
1927
+ };
1928
+ const members = [];
1929
+ const memberUnresolved = collectInlineMemberTypes(node, members, 0);
1930
+ let unionBranches;
1931
+ if (node.type === "TSUnionType") {
1932
+ const branches = [];
1933
+ for (const branch of node.types) {
1934
+ const branchType = branch.type === "TSObjectKeyword" ? "object" : extractTypeName(branch);
1935
+ if (branchType === null) return {
1936
+ type,
1937
+ typeUnresolved: true
1938
+ };
1939
+ const branchMembers = [];
1940
+ collectInlineMemberTypes(branch, branchMembers, 0);
1941
+ branches.push({
1942
+ type: branchType,
1943
+ ...branchMembers.length > 0 ? { memberTypes: [...new Set(branchMembers)] } : {}
1944
+ });
1945
+ }
1946
+ if (branches.length > 0) unionBranches = branches;
1947
+ }
1948
+ return {
1949
+ type,
1950
+ typeUnresolved: memberUnresolved,
1951
+ ...members.length > 0 ? { memberTypes: [...new Set(members)] } : {},
1952
+ ...unionBranches ? { unionBranches } : {}
1953
+ };
1954
+ }
1955
+ function collectInlineMemberTypes(node, out, depth) {
1956
+ if (depth > INLINE_MEMBER_TYPE_MAX_DEPTH) return false;
1957
+ switch (node.type) {
1958
+ case "TSTypeLiteral": {
1959
+ let unresolved = false;
1960
+ for (const member of node.members) {
1961
+ if (member.type === "TSMethodSignature") {
1962
+ out.push("Function");
1963
+ continue;
1964
+ }
1965
+ const memberAnnotation = member.typeAnnotation;
1966
+ if (!memberAnnotation) continue;
1967
+ const memberType = extractTypeName(memberAnnotation.typeAnnotation);
1968
+ if (memberType === null) {
1969
+ unresolved = true;
1970
+ continue;
1971
+ }
1972
+ out.push(memberType);
1973
+ if (collectInlineMemberTypes(memberAnnotation.typeAnnotation, out, depth + 1)) unresolved = true;
1974
+ }
1975
+ return unresolved;
1976
+ }
1977
+ case "TSArrayType": return collectInlineMemberTypes(node.elementType, out, depth + 1);
1978
+ case "TSUnionType": {
1979
+ let unresolved = false;
1980
+ for (const branch of node.types) if (collectInlineMemberTypes(branch, out, depth + 1)) unresolved = true;
1981
+ return unresolved;
1982
+ }
1983
+ case "TSTypeReference": {
1984
+ let unresolved = false;
1985
+ const typeParams = node.typeArguments?.params || node.typeParameters?.params;
1986
+ for (const param of typeParams ?? []) if (collectInlineMemberTypes(param, out, depth + 1)) unresolved = true;
1987
+ return unresolved;
1988
+ }
1989
+ default: return false;
1990
+ }
1991
+ }
1886
1992
  function extractParameter(param, sourceText) {
1887
1993
  if (param.type === "AssignmentPattern") {
1888
1994
  const left = param.left;
1889
1995
  if (left.type === "Identifier") return {
1890
1996
  name: left.name,
1891
- type: left.typeAnnotation ? extractTypeName(left.typeAnnotation.typeAnnotation) : null,
1997
+ ...describeParameterTypeFields(left.typeAnnotation),
1892
1998
  optional: true,
1893
1999
  defaultValue: sliceSource(param.right, sourceText)
1894
2000
  };
1895
2001
  if (left.type === "ObjectPattern" || left.type === "ArrayPattern") return {
1896
2002
  name: "options",
1897
- type: left.typeAnnotation ? extractTypeName(left.typeAnnotation.typeAnnotation) : "any",
2003
+ ...describeParameterTypeFields(left.typeAnnotation, "any"),
1898
2004
  optional: true,
1899
2005
  defaultValue: sliceSource(param.right, sourceText)
1900
2006
  };
@@ -1904,7 +2010,7 @@ function extractParameter(param, sourceText) {
1904
2010
  const arg = param.argument;
1905
2011
  if (arg.type === "Identifier") return {
1906
2012
  name: `...${arg.name}`,
1907
- type: param.typeAnnotation ? extractTypeName(param.typeAnnotation.typeAnnotation) : null,
2013
+ ...describeParameterTypeFields(param.typeAnnotation),
1908
2014
  optional: true,
1909
2015
  defaultValue: null
1910
2016
  };
@@ -1912,18 +2018,28 @@ function extractParameter(param, sourceText) {
1912
2018
  }
1913
2019
  if (param.type === "Identifier") return {
1914
2020
  name: param.name,
1915
- type: param.typeAnnotation ? extractTypeName(param.typeAnnotation.typeAnnotation) : null,
2021
+ ...describeParameterTypeFields(param.typeAnnotation),
1916
2022
  optional: param.optional || false,
1917
2023
  defaultValue: null
1918
2024
  };
1919
2025
  if (param.type === "ObjectPattern") return {
1920
2026
  name: "options",
1921
- type: param.typeAnnotation ? extractTypeName(param.typeAnnotation.typeAnnotation) : "any",
2027
+ ...describeParameterTypeFields(param.typeAnnotation, "any"),
1922
2028
  optional: false,
1923
2029
  defaultValue: null
1924
2030
  };
1925
2031
  return null;
1926
2032
  }
2033
+ function describeParameterTypeFields(annotation, missingAnnotationType = null) {
2034
+ if (!annotation) return { type: missingAnnotationType };
2035
+ const described = describeParameterType(annotation);
2036
+ return {
2037
+ type: described.type,
2038
+ ...described.typeUnresolved ? { typeUnresolved: true } : {},
2039
+ ...described.memberTypes ? { memberTypes: described.memberTypes } : {},
2040
+ ...described.unionBranches ? { unionBranches: described.unionBranches } : {}
2041
+ };
2042
+ }
1927
2043
  //#endregion
1928
2044
  //#region src/manifest-adapter.ts
1929
2045
  var MANIFEST_TIMESTAMP = 0;
@@ -2617,12 +2733,16 @@ var ManifestAdapter = class ManifestAdapter {
2617
2733
  name: p.name,
2618
2734
  type: p.type || "any",
2619
2735
  optional: p.optional,
2620
- default: p.defaultValue ? this.parseDefaultValue(p.defaultValue, "string") : void 0
2736
+ default: p.defaultValue ? this.parseDefaultValue(p.defaultValue, "string") : void 0,
2737
+ ...p.typeUnresolved ? { typeUnresolved: true } : {},
2738
+ ...p.memberTypes ? { memberTypes: p.memberTypes } : {},
2739
+ ...p.unionBranches ? { unionBranches: p.unionBranches } : {}
2621
2740
  })),
2622
2741
  returnType: method.returnType || "any",
2623
2742
  description: method.description || void 0,
2624
2743
  isStatic: method.isStatic,
2625
- isPublic: true
2744
+ isPublic: true,
2745
+ ...method.decoratorConfig ? { decoratorConfig: method.decoratorConfig } : {}
2626
2746
  };
2627
2747
  }
2628
2748
  /**
@@ -2977,4 +3097,4 @@ var OxcScanner = class {
2977
3097
  //#endregion
2978
3098
  export { sourceMayDeclareAgentSurface as _, parseFile as a, discoverSourceFiles as c, emptyAgentSurface as d, extractAgentSurface as f, scanSvelteAgentSurface as g, mergeAgentSurfaces as h, parseAgentSurfaceFile as i, normalizeGlobSeparators as l, isPrunedAgentSurfacePath as m, ManifestAdapter as n, parseSource as o, isAgentSurfaceSourcePath as p, extractSmrtImports as r, InheritanceResolver as s, OxcScanner as t, relativeGlobToCwd as u };
2979
3099
 
2980
- //# sourceMappingURL=scanner-C3VqXyzB.js.map
3100
+ //# sourceMappingURL=scanner-9p-PUN-X.js.map