@happyvertical/smrt-scanner 0.45.3 → 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 +15 -0
- package/dist/chunks/{scanner-C3VqXyzB.js → scanner-9p-PUN-X.js} +143 -23
- package/dist/chunks/scanner-9p-PUN-X.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +92 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +58 -0
- package/package.json +1 -1
- package/dist/chunks/scanner-C3VqXyzB.js.map +0 -1
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
|
|
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))
|
|
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":
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
3100
|
+
//# sourceMappingURL=scanner-9p-PUN-X.js.map
|