@happyvertical/smrt-scanner 0.46.0 → 0.47.1
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 +86 -2
- package/dist/chunks/{scanner-C3VqXyzB.js → scanner-CxUeI0FI.js} +185 -24
- package/dist/chunks/scanner-CxUeI0FI.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +209 -5
- package/dist/index.js +2 -2
- package/dist/types.d.ts +67 -4
- package/package.json +1 -1
- package/dist/chunks/scanner-C3VqXyzB.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -38,6 +38,9 @@ executes the source.
|
|
|
38
38
|
by `OxcScanner` and the core manifest preflight.
|
|
39
39
|
- `extractAgentSurface` / `scanSvelteAgentSurface` / `mergeAgentSurfaces` —
|
|
40
40
|
the agent-surface matcher (#2591). See below.
|
|
41
|
+
- `checkAgentSurfaceToolNames(surface, { generatedToolNames, uiToolPrefixes })`
|
|
42
|
+
— report an emitted intent whose derived WebMCP tool name a generated model
|
|
43
|
+
tool or a fixed UI tool already registers (#2725). Takes names, never config.
|
|
41
44
|
- Types (re-exported from `./types`): `RawClassDefinition`,
|
|
42
45
|
`RawFieldDefinition`, `RawMethodDefinition`, `ResolvedClassDefinition`,
|
|
43
46
|
`ScanResults`, `FileScanResult`, `OxcScannerOptions`, `InferredFieldType`,
|
|
@@ -79,7 +82,9 @@ build output can retain declarations and otherwise win duplicate resolution.
|
|
|
79
82
|
### What it refuses, always with a diagnostic
|
|
80
83
|
|
|
81
84
|
Never a silent omission — every message names `useWebMcpTool`, the escape hatch
|
|
82
|
-
for a tool set genuinely derived from computed or fetched data
|
|
85
|
+
for a tool set genuinely derived from computed or fetched data. Every code here
|
|
86
|
+
means the declaration was NOT emitted; `tool-name-collision` (below) is the one
|
|
87
|
+
exception and is advisory:
|
|
83
88
|
|
|
84
89
|
| Code | Shape |
|
|
85
90
|
|---|---|
|
|
@@ -90,6 +95,7 @@ for a tool set genuinely derived from computed or fetched data:
|
|
|
90
95
|
| `invalid-identity` | a declaration the runtime helper itself would reject: an intent `id` that is not lowercase and dot-namespaced, over 128 chars, or resolving into the reserved `smrt_ui_` namespace; an unknown declaration, target, or capability key; a malformed `capability` (bad `effect`, non-boolean flag); a `target` outside the closed `control`/`dataSurface` unions or missing a required `controlId`; a playbook with no steps, an empty/non-array/unknown-plane `planes`, an `onStepFailure` outside `abort`/`continue`, a non-boolean `enabled`, or a step whose `model` is not a qualified pair |
|
|
91
96
|
| `svelte-declaration` | written inline in a `.svelte` file |
|
|
92
97
|
| `duplicate-identity` | two modules declare the same `id`/`key`, or two intent ids derive the same WebMCP tool name |
|
|
98
|
+
| `tool-name-collision` | an EMITTED intent whose derived tool name is already registered by a generated model tool or a fixed UI tool (see below) |
|
|
93
99
|
|
|
94
100
|
Mirror `defineIntent` and `definePlaybook` validation without importing their
|
|
95
101
|
packages; update the scanner whenever those runtime rules tighten. Reject
|
|
@@ -120,6 +126,68 @@ cross-profile parity snapshot does not churn on directory order:
|
|
|
120
126
|
- paths are recorded `cwd`-relative and POSIX-separated, so a checked-in
|
|
121
127
|
artifact is neither machine- nor platform-specific.
|
|
122
128
|
|
|
129
|
+
### Collisions with names this pass does not own (#2725)
|
|
130
|
+
|
|
131
|
+
`mergeAgentSurfaces` resolves intent-vs-intent by dropping the loser, because
|
|
132
|
+
`defineIntent` REJECTS the second colliding declaration — only one can exist.
|
|
133
|
+
Two other sources register into the same document and are invisible to the
|
|
134
|
+
declaration scan, so `checkAgentSurfaceToolNames` reports them **without
|
|
135
|
+
dropping anything**:
|
|
136
|
+
|
|
137
|
+
- **generated model tools**, `${className.toLowerCase()}_${action}`, so
|
|
138
|
+
`defineIntent({ id: 'product.list' })` lands on `Product.list`;
|
|
139
|
+
- **the six fixed UI tools** under the configured `webmcp.ui.prefix`.
|
|
140
|
+
|
|
141
|
+
Warnings, not drops, and the asymmetry is the point: `defineIntent` accepts
|
|
142
|
+
these ids, so the declarations are real and belong in the artifact; the
|
|
143
|
+
document-global tool-name lock (#2613) decides which registration survives,
|
|
144
|
+
rejecting the second with a `WebMcpToolNameCollisionError` — a runtime answer to
|
|
145
|
+
a question the build can already see coming, which costs whoever loses its tool;
|
|
146
|
+
and whether it happens at all turns on runtime values no artifact records (a
|
|
147
|
+
WebMCP `namespace`, an `effects` policy, whether a page mounts both). A
|
|
148
|
+
build-time drop would guess all three, and the emitted surface would stop
|
|
149
|
+
matching the source.
|
|
150
|
+
|
|
151
|
+
Because those values are unknowable here, the generated-tool message **states
|
|
152
|
+
the precondition it assumes** — no `namespace`, action within the `effects`
|
|
153
|
+
policy — and says to disregard it when either already separates the pair. A
|
|
154
|
+
`namespace` prefixes generated tools and leaves intents alone, so it dissolves
|
|
155
|
+
the collision outright; without that sentence a namespaced app would get a
|
|
156
|
+
notice that is always wrong where it fires, recommending the remedy it had
|
|
157
|
+
already applied. `smrt doctor`'s header is `Tool name also claimed` for the same
|
|
158
|
+
reason: it must not assert past what the message says.
|
|
159
|
+
|
|
160
|
+
**It takes NAMES, never config.** The caller passes the tool names that will
|
|
161
|
+
really register; this module never derives a generated name and never applies a
|
|
162
|
+
namespace. `namespace` and `ui.prefix` are runtime `<Provider webmcp={…}>`
|
|
163
|
+
values that no build artifact records, so a build-time declaration of either
|
|
164
|
+
would be a second place to say what the provider already says, free to diverge
|
|
165
|
+
silently. Core supplies the names from `buildWebMcpToolDefinitions` — the same
|
|
166
|
+
function that emits the runtime `webMcpToolDefinitions`, and therefore already
|
|
167
|
+
filtered by the exposure policy. Comparing against every verb a class *could*
|
|
168
|
+
expose would invent collisions with tools nobody mounts.
|
|
169
|
+
|
|
170
|
+
`uiToolPrefixes` is supplied for the same reason, defaulting to the registrar's
|
|
171
|
+
`smrt_ui_`. Do NOT replace it with a prefix derived from the name: quantifying
|
|
172
|
+
over every prefix an app could have configured has, under the default, no true
|
|
173
|
+
positive at all — an id flattening to `smrt_ui_*` is rejected during extraction
|
|
174
|
+
— so every diagnostic it emits for a default-configured app is false, persisted
|
|
175
|
+
into the artifact and clearable only by renaming a correct intent. There is no
|
|
176
|
+
build-time source for `ui.prefix` (a runtime `<Provider>` prop), so this half is
|
|
177
|
+
dormant by default and the seam waits for a caller that can fill it.
|
|
178
|
+
|
|
179
|
+
`RESERVED_TOOL_NAME_PREFIX` is **not** configurable and must not become so — it
|
|
180
|
+
mirrors `defineIntent`'s own literal, which rejects the id wherever an app moved
|
|
181
|
+
its UI tools, so accepting one here would emit an entry the runtime refuses to
|
|
182
|
+
construct.
|
|
183
|
+
|
|
184
|
+
Consumers: core's Vite plugin runs the check once the manifest exists and
|
|
185
|
+
appends the diagnostics to the emitted surface; `dev:knowledge-check` maps the
|
|
186
|
+
code to the `agent-surface-tool-name-collision` warning and **excludes it from
|
|
187
|
+
the artifact-drift comparison**, which re-derives declarations from source and
|
|
188
|
+
has no manifest to produce one from; `smrt doctor` prints it under
|
|
189
|
+
`⚠️ Tool name also claimed`, separately from `Not statically emittable`.
|
|
190
|
+
|
|
123
191
|
The scanner mirrors the #2587 capability vocabulary structurally instead of
|
|
124
192
|
importing `@happyvertical/smrt-types`: core depends on this package, so the
|
|
125
193
|
reverse edge would close a cycle. Core reconciles the two shapes in exactly one
|
|
@@ -160,7 +228,8 @@ place, `toKnowledgeAgentSurface` in `vite-plugin/index.ts`.
|
|
|
160
228
|
field-type inference.
|
|
161
229
|
- `src/verify-completeness.ts` — `verifyManifestCompleteness` publish guard.
|
|
162
230
|
- `src/agent-surface.ts` — the `defineIntent` / `definePlaybook` matcher, its
|
|
163
|
-
diagnostics,
|
|
231
|
+
diagnostics, `mergeAgentSurfaces` (#2591), and `checkAgentSurfaceToolNames`
|
|
232
|
+
(#2725).
|
|
164
233
|
- `src/source-location.ts` — `getLineColumn`, split out so `agent-surface.ts`
|
|
165
234
|
can resolve a diagnostic's position without importing `oxc-parser.ts`, which
|
|
166
235
|
imports it back.
|
|
@@ -214,6 +283,21 @@ place, `toKnowledgeAgentSurface` in `vite-plugin/index.ts`.
|
|
|
214
283
|
printed by the Vite plugin, carried into `smrt-knowledge.json`, reported by
|
|
215
284
|
`smrt doctor`, and surfaced by `dev:knowledge-check` as
|
|
216
285
|
`agent-surface-not-static`.
|
|
286
|
+
- **A parameter carries type PROVENANCE, a field does not.**
|
|
287
|
+
`RawParameterDefinition.typeUnresolved` marks an annotation the scanner could
|
|
288
|
+
not express (intersection, tuple, conditional, mapped, `typeof`, indexed
|
|
289
|
+
access) — `type` still reads `'any'` for compatibility, so that flag is the
|
|
290
|
+
only thing separating it from an authored `any`. `memberTypes` carries the
|
|
291
|
+
members of an INLINE object literal, which `extractTypeName` otherwise
|
|
292
|
+
flattens to `'object'`; NAMED bags stay unexpanded on purpose. Core's API
|
|
293
|
+
wire-ability gate fails closed on both (#2686). The bare `object` keyword is
|
|
294
|
+
resolved for parameters only — `ManifestAdapter.inferFromAnnotation` gives an
|
|
295
|
+
`object`-typed FIELD a `{}` column default, so naming it in `extractTypeName`
|
|
296
|
+
would silently change DDL.
|
|
297
|
+
- **`@method()` config is read with `requireLiteralValues`**, like `@smrt()`: an
|
|
298
|
+
unresolvable value is a scan error, because a dropped `expose: false` restores
|
|
299
|
+
the routing the author was withholding. `decoratorConfig` is `undefined` for
|
|
300
|
+
an undecorated method and `{}` for a bare `@method()`; the two differ.
|
|
217
301
|
- **Relationship targets are resolved, not copied**: `@foreignKey`,
|
|
218
302
|
`@oneToMany` and `@manyToMany` arguments arrive as raw source text.
|
|
219
303
|
`'Target'`/`Target` pass through and a forward-reference thunk
|
|
@@ -29,6 +29,16 @@ var INTENT_ID_PATTERN = /^[a-z][a-z0-9]*(?:\.[a-z0-9][a-z0-9_]*)+$/;
|
|
|
29
29
|
var INTENT_ID_MAX_LENGTH = 128;
|
|
30
30
|
var DESCRIPTION_MAX_LENGTH = 1024;
|
|
31
31
|
var RESERVED_TOOL_NAME_PREFIX = "smrt_ui_";
|
|
32
|
+
var FIXED_UI_TOOL_SUFFIXES = [
|
|
33
|
+
"execute_data_surface_control",
|
|
34
|
+
"execute_form_control",
|
|
35
|
+
"inspect_data_surface",
|
|
36
|
+
"inspect_form_control",
|
|
37
|
+
"list_data_surfaces",
|
|
38
|
+
"list_form_controls"
|
|
39
|
+
];
|
|
40
|
+
var UI_TOOL_PREFIX_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
41
|
+
var DEFAULT_UI_TOOL_PREFIX = "smrt_ui_";
|
|
32
42
|
var INTENT_DECLARATION_KEYS = /* @__PURE__ */ new Set([
|
|
33
43
|
"id",
|
|
34
44
|
"description",
|
|
@@ -668,6 +678,37 @@ function mergeAgentSurfaces(surfaces, relativize = (filePath) => filePath) {
|
|
|
668
678
|
diagnostics: diagnostics.sort((a, b) => compareStrings(a.filePath, b.filePath) || (a.line ?? 0) - (b.line ?? 0) || (a.column ?? 0) - (b.column ?? 0) || compareStrings(a.code, b.code) || compareStrings(a.message, b.message))
|
|
669
679
|
};
|
|
670
680
|
}
|
|
681
|
+
function checkAgentSurfaceToolNames(surface, options = {}) {
|
|
682
|
+
const diagnostics = [];
|
|
683
|
+
const generated = /* @__PURE__ */ new Map();
|
|
684
|
+
for (const tool of options.generatedToolNames ?? []) if (!generated.has(tool.name)) generated.set(tool.name, tool);
|
|
685
|
+
const uiTools = /* @__PURE__ */ new Map();
|
|
686
|
+
for (const prefix of options.uiToolPrefixes ?? [DEFAULT_UI_TOOL_PREFIX]) {
|
|
687
|
+
if (!UI_TOOL_PREFIX_PATTERN.test(prefix)) continue;
|
|
688
|
+
for (const suffix of FIXED_UI_TOOL_SUFFIXES) if (!uiTools.has(`${prefix}${suffix}`)) uiTools.set(`${prefix}${suffix}`, prefix);
|
|
689
|
+
}
|
|
690
|
+
for (const intent of surface.intents) {
|
|
691
|
+
const toolName = intentToolName(intent.id);
|
|
692
|
+
const collidingTool = generated.get(toolName);
|
|
693
|
+
if (collidingTool) {
|
|
694
|
+
const owner = collidingTool.declaredBy ? `\`${collidingTool.declaredBy}\`` : "a generated model action";
|
|
695
|
+
diagnostics.push({
|
|
696
|
+
code: "tool-name-collision",
|
|
697
|
+
helper: "defineIntent",
|
|
698
|
+
message: `view intent \`${intent.id}\` derives the WebMCP tool name \`${toolName}\`, which is also the generated model tool for ${owner}. Both are emitted \u2014 \`defineIntent\` accepts the id, so the declaration is real. On a page that mounts both with no WebMCP \`namespace\`, the document-global tool-name lock rejects whichever registers second with a \`WebMcpToolNameCollisionError\`. A build cannot see the provider's \`namespace\` or \`effects\` policy, so if either already separates this pair, disregard this. Otherwise set a \`namespace\`, which prefixes the generated tools and leaves intents alone, or rename the intent.`,
|
|
699
|
+
filePath: intent.filePath
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
const uiPrefix = uiTools.get(toolName);
|
|
703
|
+
if (uiPrefix !== void 0) diagnostics.push({
|
|
704
|
+
code: "tool-name-collision",
|
|
705
|
+
helper: "defineIntent",
|
|
706
|
+
message: `view intent \`${intent.id}\` derives the WebMCP tool name \`${toolName}\`, which is also one of the six fixed UI tools mounted under \`ui.prefix\` \`${uiPrefix}\`. \`defineIntent\` accepts the id because it reserves only the default \`smrt_ui_\` prefix, so the two reach the document-global tool-name lock and whichever registers second is rejected with a \`WebMcpToolNameCollisionError\`. Rename the intent, or give the fixed UI tools a different \`ui.prefix\`.`,
|
|
707
|
+
filePath: intent.filePath
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
return diagnostics.sort((a, b) => compareStrings(a.filePath, b.filePath) || compareStrings(a.code, b.code) || compareStrings(a.message, b.message));
|
|
711
|
+
}
|
|
671
712
|
function emptyAgentSurface() {
|
|
672
713
|
return {
|
|
673
714
|
intents: [],
|
|
@@ -1080,7 +1121,8 @@ function parseFile(filePath) {
|
|
|
1080
1121
|
smrtImports = extractSmrtImports(program.body);
|
|
1081
1122
|
const ctx = {
|
|
1082
1123
|
constants: extractModuleObjectConstants(program.body, sourceText),
|
|
1083
|
-
unresolved: []
|
|
1124
|
+
unresolved: [],
|
|
1125
|
+
importAliases
|
|
1084
1126
|
};
|
|
1085
1127
|
for (const node of program.body) {
|
|
1086
1128
|
const extracted = extractClassFromNode(node, filePath, sourceText, importAliases, ctx);
|
|
@@ -1164,7 +1206,8 @@ function parseSource(sourceText, filename = "test.ts") {
|
|
|
1164
1206
|
smrtImports = extractSmrtImports(program.body);
|
|
1165
1207
|
const ctx = {
|
|
1166
1208
|
constants: extractModuleObjectConstants(program.body, sourceText),
|
|
1167
|
-
unresolved: []
|
|
1209
|
+
unresolved: [],
|
|
1210
|
+
importAliases
|
|
1168
1211
|
};
|
|
1169
1212
|
for (const node of program.body) {
|
|
1170
1213
|
const extracted = extractClassFromNode(node, filename, sourceText, importAliases, ctx);
|
|
@@ -1491,7 +1534,7 @@ function reportUnresolvedSpreads(unresolved, filePath, sourceText, errors) {
|
|
|
1491
1534
|
for (const spread of unresolved) {
|
|
1492
1535
|
const loc = spread.start === void 0 ? void 0 : getLineColumn(sourceText, spread.start);
|
|
1493
1536
|
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
|
|
1537
|
+
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
1538
|
filePath,
|
|
1496
1539
|
line: loc?.line,
|
|
1497
1540
|
column: loc?.column,
|
|
@@ -1508,9 +1551,9 @@ function extractClassFromNode(node, filePath, sourceText, importAliases, ctx) {
|
|
|
1508
1551
|
function extractClassDeclaration(node, filePath, sourceText, importAliases, ctx) {
|
|
1509
1552
|
const className = node.id?.name || "AnonymousClass";
|
|
1510
1553
|
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"));
|
|
1554
|
+
const smrtDecorator = decorators.find((d) => isSmrtDecorator(d, importAliases));
|
|
1555
|
+
const reportDecorator = decorators.find((d) => isNamedDecorator(d, "report", importAliases));
|
|
1556
|
+
const tenantScopedDecorator = decorators.find((d) => isNamedDecorator(d, "TenantScoped", importAliases));
|
|
1514
1557
|
const hasSmartDecorator = !!smrtDecorator;
|
|
1515
1558
|
const smrtConfig = smrtDecorator ? extractDecoratorConfig(smrtDecorator, sourceText, ctx ? {
|
|
1516
1559
|
...ctx,
|
|
@@ -1528,7 +1571,7 @@ function extractClassDeclaration(node, filePath, sourceText, importAliases, ctx)
|
|
|
1528
1571
|
const field = extractPropertyDefinition(member, sourceText);
|
|
1529
1572
|
if (field) fields.push(field);
|
|
1530
1573
|
} else if (member.type === "MethodDefinition") {
|
|
1531
|
-
const method = extractMethodDefinition(member, sourceText);
|
|
1574
|
+
const method = extractMethodDefinition(member, sourceText, ctx);
|
|
1532
1575
|
if (method) methods.push(method);
|
|
1533
1576
|
}
|
|
1534
1577
|
return {
|
|
@@ -1544,16 +1587,17 @@ function extractClassDeclaration(node, filePath, sourceText, importAliases, ctx)
|
|
|
1544
1587
|
endLine: node.loc?.end.line || 1
|
|
1545
1588
|
};
|
|
1546
1589
|
}
|
|
1547
|
-
function isSmrtDecorator(decorator) {
|
|
1548
|
-
return isNamedDecorator(decorator, "smrt");
|
|
1590
|
+
function isSmrtDecorator(decorator, importAliases) {
|
|
1591
|
+
return isNamedDecorator(decorator, "smrt", importAliases);
|
|
1549
1592
|
}
|
|
1550
|
-
function isNamedDecorator(decorator, name) {
|
|
1593
|
+
function isNamedDecorator(decorator, name, importAliases) {
|
|
1551
1594
|
const expr = decorator.expression;
|
|
1595
|
+
const resolve = (local) => importAliases?.get(local) ?? local;
|
|
1552
1596
|
if (expr.type === "CallExpression") {
|
|
1553
1597
|
const callee = expr.callee;
|
|
1554
|
-
if (callee.type === "Identifier" && callee.name === name) return true;
|
|
1598
|
+
if (callee.type === "Identifier" && resolve(callee.name) === name) return true;
|
|
1555
1599
|
}
|
|
1556
|
-
if (expr.type === "Identifier" && expr.name === name) return true;
|
|
1600
|
+
if (expr.type === "Identifier" && resolve(expr.name) === name) return true;
|
|
1557
1601
|
return false;
|
|
1558
1602
|
}
|
|
1559
1603
|
function extractDecoratorConfig(decorator, sourceText, ctx) {
|
|
@@ -1757,7 +1801,8 @@ function extractTypeName(type) {
|
|
|
1757
1801
|
else if (type.typeName.type === "TSQualifiedName") baseName = getQualifiedName(type.typeName);
|
|
1758
1802
|
const typeParams = type.typeArguments?.params || type.typeParameters?.params;
|
|
1759
1803
|
if (baseName && typeParams?.length) {
|
|
1760
|
-
const typeArgs = typeParams.map((p) => extractTypeName(p))
|
|
1804
|
+
const typeArgs = typeParams.map((p) => extractTypeName(p));
|
|
1805
|
+
if (typeArgs.some((arg) => arg === null)) return null;
|
|
1761
1806
|
if (typeArgs.length > 0) return `${baseName}<${typeArgs.join(", ")}>`;
|
|
1762
1807
|
}
|
|
1763
1808
|
return baseName;
|
|
@@ -1766,7 +1811,12 @@ function extractTypeName(type) {
|
|
|
1766
1811
|
case "TSNumberKeyword": return "number";
|
|
1767
1812
|
case "TSBooleanKeyword": return "boolean";
|
|
1768
1813
|
case "TSAnyKeyword": return "any";
|
|
1814
|
+
case "TSUnknownKeyword": return "unknown";
|
|
1815
|
+
case "TSNeverKeyword": return "never";
|
|
1816
|
+
case "TSBigIntKeyword": return "bigint";
|
|
1817
|
+
case "TSSymbolKeyword": return "symbol";
|
|
1769
1818
|
case "TSVoidKeyword": return "void";
|
|
1819
|
+
case "TSThisType": return "this";
|
|
1770
1820
|
case "TSNullKeyword": return "null";
|
|
1771
1821
|
case "TSUndefinedKeyword": return "undefined";
|
|
1772
1822
|
case "TSLiteralType": {
|
|
@@ -1781,7 +1831,11 @@ function extractTypeName(type) {
|
|
|
1781
1831
|
const elementType = extractTypeName(type.elementType);
|
|
1782
1832
|
return elementType ? `${elementType}[]` : null;
|
|
1783
1833
|
}
|
|
1784
|
-
case "TSUnionType":
|
|
1834
|
+
case "TSUnionType": {
|
|
1835
|
+
const types = type.types.map((t) => extractTypeName(t));
|
|
1836
|
+
if (types.some((branch) => branch === null)) return null;
|
|
1837
|
+
return types.join(" | ");
|
|
1838
|
+
}
|
|
1785
1839
|
case "TSTypeLiteral": return "object";
|
|
1786
1840
|
case "TSFunctionType": return "Function";
|
|
1787
1841
|
default: return null;
|
|
@@ -1861,7 +1915,7 @@ function extractFieldDecorator(decorator, sourceText) {
|
|
|
1861
1915
|
arguments: args
|
|
1862
1916
|
};
|
|
1863
1917
|
}
|
|
1864
|
-
function extractMethodDefinition(node, sourceText) {
|
|
1918
|
+
function extractMethodDefinition(node, sourceText, ctx) {
|
|
1865
1919
|
if (node.kind !== "method") return null;
|
|
1866
1920
|
const name = getPropertyKey(node.key);
|
|
1867
1921
|
if (!name) return null;
|
|
@@ -1872,6 +1926,7 @@ function extractMethodDefinition(node, sourceText) {
|
|
|
1872
1926
|
if (extracted) parameters.push(extracted);
|
|
1873
1927
|
}
|
|
1874
1928
|
const returnType = func.returnType ? extractTypeName(func.returnType.typeAnnotation) : null;
|
|
1929
|
+
const decoratorConfig = extractMethodDecoratorConfig(node, sourceText, ctx);
|
|
1875
1930
|
return {
|
|
1876
1931
|
name,
|
|
1877
1932
|
async: func.async,
|
|
@@ -1880,21 +1935,113 @@ function extractMethodDefinition(node, sourceText) {
|
|
|
1880
1935
|
parameters,
|
|
1881
1936
|
returnType,
|
|
1882
1937
|
description: null,
|
|
1938
|
+
...decoratorConfig ? { decoratorConfig } : {},
|
|
1883
1939
|
line: node.loc?.start.line || 0
|
|
1884
1940
|
};
|
|
1885
1941
|
}
|
|
1942
|
+
function extractMethodDecoratorConfig(node, sourceText, ctx) {
|
|
1943
|
+
const decorator = node.decorators?.find((d) => isNamedDecorator(d, "method", ctx?.importAliases));
|
|
1944
|
+
if (!decorator) return void 0;
|
|
1945
|
+
const unresolvedBefore = ctx?.unresolved.length ?? 0;
|
|
1946
|
+
const config = extractDecoratorConfig(decorator, sourceText, ctx ? {
|
|
1947
|
+
...ctx,
|
|
1948
|
+
requireLiteralValues: true
|
|
1949
|
+
} : ctx);
|
|
1950
|
+
if (ctx) for (const entry of ctx.unresolved.slice(unresolvedBefore)) entry.decorator = "@method()";
|
|
1951
|
+
return config ?? {};
|
|
1952
|
+
}
|
|
1953
|
+
var INLINE_MEMBER_TYPE_MAX_DEPTH = 4;
|
|
1954
|
+
function describeParameterType(annotation) {
|
|
1955
|
+
if (!annotation) return {
|
|
1956
|
+
type: null,
|
|
1957
|
+
typeUnresolved: false
|
|
1958
|
+
};
|
|
1959
|
+
const node = annotation.typeAnnotation;
|
|
1960
|
+
if (node.type === "TSObjectKeyword") return {
|
|
1961
|
+
type: "object",
|
|
1962
|
+
typeUnresolved: false
|
|
1963
|
+
};
|
|
1964
|
+
const type = extractTypeName(node);
|
|
1965
|
+
if (type === null) return {
|
|
1966
|
+
type: null,
|
|
1967
|
+
typeUnresolved: true
|
|
1968
|
+
};
|
|
1969
|
+
const members = [];
|
|
1970
|
+
const memberUnresolved = collectInlineMemberTypes(node, members, 0);
|
|
1971
|
+
let unionBranches;
|
|
1972
|
+
if (node.type === "TSUnionType") {
|
|
1973
|
+
const branches = [];
|
|
1974
|
+
for (const branch of node.types) {
|
|
1975
|
+
const branchType = branch.type === "TSObjectKeyword" ? "object" : extractTypeName(branch);
|
|
1976
|
+
if (branchType === null) return {
|
|
1977
|
+
type,
|
|
1978
|
+
typeUnresolved: true
|
|
1979
|
+
};
|
|
1980
|
+
const branchMembers = [];
|
|
1981
|
+
collectInlineMemberTypes(branch, branchMembers, 0);
|
|
1982
|
+
branches.push({
|
|
1983
|
+
type: branchType,
|
|
1984
|
+
...branchMembers.length > 0 ? { memberTypes: [...new Set(branchMembers)] } : {}
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
if (branches.length > 0) unionBranches = branches;
|
|
1988
|
+
}
|
|
1989
|
+
return {
|
|
1990
|
+
type,
|
|
1991
|
+
typeUnresolved: memberUnresolved,
|
|
1992
|
+
...members.length > 0 ? { memberTypes: [...new Set(members)] } : {},
|
|
1993
|
+
...unionBranches ? { unionBranches } : {}
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
function collectInlineMemberTypes(node, out, depth) {
|
|
1997
|
+
if (depth > INLINE_MEMBER_TYPE_MAX_DEPTH) return false;
|
|
1998
|
+
switch (node.type) {
|
|
1999
|
+
case "TSTypeLiteral": {
|
|
2000
|
+
let unresolved = false;
|
|
2001
|
+
for (const member of node.members) {
|
|
2002
|
+
if (member.type === "TSMethodSignature") {
|
|
2003
|
+
out.push("Function");
|
|
2004
|
+
continue;
|
|
2005
|
+
}
|
|
2006
|
+
const memberAnnotation = member.typeAnnotation;
|
|
2007
|
+
if (!memberAnnotation) continue;
|
|
2008
|
+
const memberType = extractTypeName(memberAnnotation.typeAnnotation);
|
|
2009
|
+
if (memberType === null) {
|
|
2010
|
+
unresolved = true;
|
|
2011
|
+
continue;
|
|
2012
|
+
}
|
|
2013
|
+
out.push(memberType);
|
|
2014
|
+
if (collectInlineMemberTypes(memberAnnotation.typeAnnotation, out, depth + 1)) unresolved = true;
|
|
2015
|
+
}
|
|
2016
|
+
return unresolved;
|
|
2017
|
+
}
|
|
2018
|
+
case "TSArrayType": return collectInlineMemberTypes(node.elementType, out, depth + 1);
|
|
2019
|
+
case "TSUnionType": {
|
|
2020
|
+
let unresolved = false;
|
|
2021
|
+
for (const branch of node.types) if (collectInlineMemberTypes(branch, out, depth + 1)) unresolved = true;
|
|
2022
|
+
return unresolved;
|
|
2023
|
+
}
|
|
2024
|
+
case "TSTypeReference": {
|
|
2025
|
+
let unresolved = false;
|
|
2026
|
+
const typeParams = node.typeArguments?.params || node.typeParameters?.params;
|
|
2027
|
+
for (const param of typeParams ?? []) if (collectInlineMemberTypes(param, out, depth + 1)) unresolved = true;
|
|
2028
|
+
return unresolved;
|
|
2029
|
+
}
|
|
2030
|
+
default: return false;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
1886
2033
|
function extractParameter(param, sourceText) {
|
|
1887
2034
|
if (param.type === "AssignmentPattern") {
|
|
1888
2035
|
const left = param.left;
|
|
1889
2036
|
if (left.type === "Identifier") return {
|
|
1890
2037
|
name: left.name,
|
|
1891
|
-
|
|
2038
|
+
...describeParameterTypeFields(left.typeAnnotation),
|
|
1892
2039
|
optional: true,
|
|
1893
2040
|
defaultValue: sliceSource(param.right, sourceText)
|
|
1894
2041
|
};
|
|
1895
2042
|
if (left.type === "ObjectPattern" || left.type === "ArrayPattern") return {
|
|
1896
2043
|
name: "options",
|
|
1897
|
-
|
|
2044
|
+
...describeParameterTypeFields(left.typeAnnotation, "any"),
|
|
1898
2045
|
optional: true,
|
|
1899
2046
|
defaultValue: sliceSource(param.right, sourceText)
|
|
1900
2047
|
};
|
|
@@ -1904,7 +2051,7 @@ function extractParameter(param, sourceText) {
|
|
|
1904
2051
|
const arg = param.argument;
|
|
1905
2052
|
if (arg.type === "Identifier") return {
|
|
1906
2053
|
name: `...${arg.name}`,
|
|
1907
|
-
|
|
2054
|
+
...describeParameterTypeFields(param.typeAnnotation),
|
|
1908
2055
|
optional: true,
|
|
1909
2056
|
defaultValue: null
|
|
1910
2057
|
};
|
|
@@ -1912,18 +2059,28 @@ function extractParameter(param, sourceText) {
|
|
|
1912
2059
|
}
|
|
1913
2060
|
if (param.type === "Identifier") return {
|
|
1914
2061
|
name: param.name,
|
|
1915
|
-
|
|
2062
|
+
...describeParameterTypeFields(param.typeAnnotation),
|
|
1916
2063
|
optional: param.optional || false,
|
|
1917
2064
|
defaultValue: null
|
|
1918
2065
|
};
|
|
1919
2066
|
if (param.type === "ObjectPattern") return {
|
|
1920
2067
|
name: "options",
|
|
1921
|
-
|
|
2068
|
+
...describeParameterTypeFields(param.typeAnnotation, "any"),
|
|
1922
2069
|
optional: false,
|
|
1923
2070
|
defaultValue: null
|
|
1924
2071
|
};
|
|
1925
2072
|
return null;
|
|
1926
2073
|
}
|
|
2074
|
+
function describeParameterTypeFields(annotation, missingAnnotationType = null) {
|
|
2075
|
+
if (!annotation) return { type: missingAnnotationType };
|
|
2076
|
+
const described = describeParameterType(annotation);
|
|
2077
|
+
return {
|
|
2078
|
+
type: described.type,
|
|
2079
|
+
...described.typeUnresolved ? { typeUnresolved: true } : {},
|
|
2080
|
+
...described.memberTypes ? { memberTypes: described.memberTypes } : {},
|
|
2081
|
+
...described.unionBranches ? { unionBranches: described.unionBranches } : {}
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
1927
2084
|
//#endregion
|
|
1928
2085
|
//#region src/manifest-adapter.ts
|
|
1929
2086
|
var MANIFEST_TIMESTAMP = 0;
|
|
@@ -2617,12 +2774,16 @@ var ManifestAdapter = class ManifestAdapter {
|
|
|
2617
2774
|
name: p.name,
|
|
2618
2775
|
type: p.type || "any",
|
|
2619
2776
|
optional: p.optional,
|
|
2620
|
-
default: p.defaultValue ? this.parseDefaultValue(p.defaultValue, "string") : void 0
|
|
2777
|
+
default: p.defaultValue ? this.parseDefaultValue(p.defaultValue, "string") : void 0,
|
|
2778
|
+
...p.typeUnresolved ? { typeUnresolved: true } : {},
|
|
2779
|
+
...p.memberTypes ? { memberTypes: p.memberTypes } : {},
|
|
2780
|
+
...p.unionBranches ? { unionBranches: p.unionBranches } : {}
|
|
2621
2781
|
})),
|
|
2622
2782
|
returnType: method.returnType || "any",
|
|
2623
2783
|
description: method.description || void 0,
|
|
2624
2784
|
isStatic: method.isStatic,
|
|
2625
|
-
isPublic: true
|
|
2785
|
+
isPublic: true,
|
|
2786
|
+
...method.decoratorConfig ? { decoratorConfig: method.decoratorConfig } : {}
|
|
2626
2787
|
};
|
|
2627
2788
|
}
|
|
2628
2789
|
/**
|
|
@@ -2975,6 +3136,6 @@ var OxcScanner = class {
|
|
|
2975
3136
|
}
|
|
2976
3137
|
};
|
|
2977
3138
|
//#endregion
|
|
2978
|
-
export {
|
|
3139
|
+
export { scanSvelteAgentSurface as _, parseFile as a, discoverSourceFiles as c, checkAgentSurfaceToolNames as d, emptyAgentSurface as f, mergeAgentSurfaces as g, isPrunedAgentSurfacePath as h, parseAgentSurfaceFile as i, normalizeGlobSeparators as l, isAgentSurfaceSourcePath as m, ManifestAdapter as n, parseSource as o, extractAgentSurface as p, extractSmrtImports as r, InheritanceResolver as s, OxcScanner as t, relativeGlobToCwd as u, sourceMayDeclareAgentSurface as v };
|
|
2979
3140
|
|
|
2980
|
-
//# sourceMappingURL=scanner-
|
|
3141
|
+
//# sourceMappingURL=scanner-CxUeI0FI.js.map
|