@jskit-ai/architecture-inspector
v0.1.13
Published
Static Vue architecture inspector for the portable architecture-inspection command contract.
Readme
Architecture Inspector
@jskit-ai/architecture-inspector/vue exports
inspectVueArchitecture({ projectRoot, input, aliases, httpScope, apiBasePath, calls, apiPrefixAliases })
for finite static architecture inspection, and ships the jskit-inspect-vue
command and the Vue Preview locator src/previewLocator.js. It is a plain tool
package, not a JSKIT framework runtime package.
Architecture inspection
Imported assets remain file-import evidence. The Vue adapter parses only its supported JavaScript, TypeScript and Vue modules; binary images do not block route discovery. Asset-to-component transformations require their own adapter and remain explicitly unsupported here.
Run jskit-inspect-vue in the project directory. It reads one bounded JSON
document from stdin and writes one genesis.architecture-observations.v1 JSON
document to stdout. Errors use stderr and a failing exit status. It does not
import application code, start a server, generate router declarations or retain
a watcher. The package imports no consumer's code: the input contract is its
own, and it writes the observation format its consumer defines, which it names
only by schema id. The command runs the
inspection in a child Node process whose old generation is bounded by the size
of the declared scripts (192 MB plus 56 MB per MiB), so memory stays near what
the inspection holds; a caller that starts Node with its own
--max-old-space-size keeps that bound and runs in place.
Input supplies schema: "jskit.architecture-inspection-input.v1", indexed
files: [{ path, hash }] and uiDiscovery.discoveries. Optional packages
carry id, name, manifest, declarations, root, opaque and public
entryPoints: [{ importPath, file }]; packageIdentity binds observations to
the same inspected public boundaries. A package resolves for the files under
each manifest that declares it (declarations: [{ manifest }], or its
manifest when it has none), whatever the dependency kind, so the packages of
a workspace resolve one another when each workspace manifest is declared. Paths are project-relative
and hashes use sha256:<hex>. Only declared, hash-matching UTF-8 files are read;
symlinks and files over 4 MiB are rejected. Optional stackHash and
cacheDirectory serve the call cache below; other unknown fields are ignored.
Each discovery supplies id, adapter, surface, mode, registrations,
pageRoots and componentRoots. Supported adapters are jskit,
vue-registered-router and vue-filesystem-router.
Registered mode reads literal Vue Router
createRouter({ routes })declarations from the registration files. Static local arrays and literal child routes work.Filesystem mode reads literal
VueRouter(...)options from the registration files and uses Vue Router's own scanner with declarations and watchers disabled. Its actualroutesFolderdetermines pages; a directory alone is not proof. Sibling routes are visited infullPathorder, so registration ids do not depend on filesystem scan order.Template component uses come from actual referenced imports. Shared definitions retain their source identity. Unused imports create no composition relationship.
Imported external elements retain the declared package/import-path/symbol owner. Their implementation files are not read. Project-owned workspace exports can resolve to indexed source files.
Static imports from indexed JavaScript, TypeScript and Vue files also provide optional
importsobservations. The framework resolver owns declared aliases, root-relative paths and file resolution. Each record retains the source range, current input hashes, target file and imported names, including aliases,default, namespace*and type-only classification. These are import facts, not evidence of runtime calls. Coverage remains partial; namespace use does not establish which individual exports are consumed.An HTTP scope names the application's operations and enables scoped literal
fetchuses and the routes adefineFeaturesetup registers on its HTTP router. It is the rootpackage.jsonname, or--http-scope shopwhen the operations should carry another name; without either, HTTP observations stay unconfigured. Use--alias @=srcfor declared project aliases.router.register(method, path, options, handler)becomes an entry operation whose controller is the handler, when the router ishttp.routerof the setup's destructuredhttpdependency (ordependencies.http.router), used in the setup or passed to a project function that registers on the parameter receiving it. Such functions may be local or imported through named imports and re-exports, at most four calls deep. The method and path must be literals, local constants or template literals of them; a call of the kernel'sresolveScopedApiBasePathwith literal options stands for the path it returns. Anything else is reported asCONTROLLER_UNRESOLVED.--api-base-path /apiprefixes these paths.Actual
CrudListScreen/CrudAddEditScreenuses whose bound screen comes from the matching JSKIT composable can connect to exporteddefineCrudFeaturecontroller declarations. The resource must resolve through indexed imports and named re-exports to a literaldefineCrudContractdeclaration with literal enabled operations, and a literal namespace or, without one, a table resource whosedefineTableResourcename is literal. Scope includes the explicit HTTP application identity, declared surface and API suffix; the key usesjskit.crudresource operations rather than claiming a fully expanded runtime URL. Custom clients, transport/resource overrides, dynamic operations and disabled routes do not create guessed links. This shows declared entry points; runtime provider activation is not proven. Each such entry operation carries theactionIdits route runs,crud.<namespace>.<operation>. The declaration's customactions(a literal object, bound locally or imported through named re-exports) that declare a literalroutebecomehttpentry operations at the declaration's API base path (scope.routeBaseandrelativePath) plus the route'spath, with the action'sid, orcrud.<namespace>.<name>without one. Other forms are reported asCRUD_ACTION_ROUTE_UNRESOLVED.Routes registered through the kernel's
createActionRouteScopebecome entry operations with their full path and, foractionRoute, theiractionId. The scope options must be string literals or imported constant strings; each route's method, suffix andactionIdmust be literals, template literals or elements of an enclosing literal array's.maporfor...of(at most 64). Anything else is reported asACTION_ROUTE_UNRESOLVEDorACTION_ROUTE_SCOPE_UNBOUND, never guessed.
Function calls (--calls)
--calls (library option calls: true) adds functions, calls and
entryPoints and their capabilities. Without it the output is unchanged.
- Functions. Every function-like with a body, plus each file's top-level
code as
fn:<path>#module. Other ids arefn:<path>#<line>:<column>at the syntax node's start after decorators,exportand comments, the same position Genesis's syntax sites use.exported: truemarks exported top-level functions;containernames an enclosing class or object. - Calls. One TypeScript program over the declared scripts and Vue script
blocks only, plus TypeScript's own libraries; nothing under
node_modulesis loaded. Each call isexact(checker,di-binding,self,route-actionor single-valuedargument-flow),probable(otherargument-flow,receiver-name, or several candidates) orunresolved.defineFeatureinjection bindsrequirestokens to thesetupoutputs thatprovidesthem: one provider is exact, several are ambiguous.- Argument flow follows the caller. A function passed to a parameter that
the callee calls while it runs (in its own body, in callbacks it passes
on, or by forwarding it to a function that does) is an
argument-flowcall of the function that passes it, recorded at that call. The callee's own call of the parameter isunresolved, so one caller's callback is never reached through another caller. A parameter called only from a closure the callee stores or returns keeps every value its callers pass. Argument flow is exact when it names one function for certain: a passed inline function or a name bound to one function, or a parameter (also a destructured factory parameter such ascreateActions({ sessions })) that receives one value across all the project's calls, when that value resolves exactly. Several values stay probable. - JSDoc types are declared types:
@param,@type,@typedefandimport("...")types, including a package's./server/typesreached through its declared entry points. A call through a parameter or binding typed that way is acheckercall. A member a type declares astypeofa function ({ run: typeof run },@property {typeof run} run) is that function. - Action definitions (
{ id, execute }in a feature'sactions, also throughcreate*Actionsfactories, pass-through wrappers and literal-array.map, with ids that are literals or module constants) and the custom actions of a CRUD declaration becomeactionentry points;request.executeAction({ actionId: "..." })reaches their execute function, and thecontrollerFunctionIdof each entry operation that names an action is that function. - Calls that are provably external are left out: package imports, builtins,
values produced by them (including
letassignments,for...of, destructuring and callbacks they call), Vue's<script setup>macros andemit. - A method call on an untyped receiver (
receiver-name) reaches a project function only when exactly one function on that side declares the name (or exactly one in a file the receiver names) and no builtin member has it: the members of TypeScript's libraries and of Node'sEventEmitter,fs.Statsand streams. Otherwise it isunresolved, and a builtin name no project function declares is left out. - At most 2,000 call records per file; beyond that
CALLS_TRUNCATEDand apartialcalls capability.
- Transports. With an HTTP scope,
useEndpointResource,useCommand,getHttpWebClient().requestand clientfetchbecome operation uses that carryfromFunctionId;parentIdis set only in page and component files. Paths are partially evaluated (template literals, constants, imports, parameter defaults,usePaths().api,resolveScopedApiBasePath). A key iskeyCertainty: "exact"when every route literal is matched; two or three candidate routes give oneprobableuse each, with ids suffixed per candidate.--api-prefix-alias /api/x=/api/app/:slug/x(repeatable) rewrites client prefixes before matching. - Cache. With an absolute, existing
cacheDirectoryoutside the project, the command keepsjskit-calls.jsonthere. An unchanged rerun reuses the whole result. Otherwise a file keeps its calls when its own and its directly imported sources are unchanged and every target still exists. Calls that injection or argument flow resolve depend on other files' arguments, so they are resolved again on every run; a file whose such calls now reach other functions is resolved again, with every reused file whose calls reach it. Any change to a manifest, lockfile,tsconfig/jsconfig, the inspector's own manifest or source (even without a version bump), packages, UI discovery, options orstackHashrebuilds everything. An absent, unusable or corrupt cache means a cold run.
Preview locator
src/previewLocator.js is a classic script a Preview can inline. It defines
globalThis.__jskitLocateElement(element), which returns { file, name? } for
the nearest component with a source file (Vue development builds record it as
__file) that rendered the element, or null for plain DOM. It reads only
Vue's component bookkeeping, never text content or values.
Dynamic routing, template component tokens, runtime placement predicates, application request wrappers and dispatcher conventions require more evidence or an application-owned inspector. They remain partial or unsupported, never guessed. This command does not claim complete runtime call analysis. Protocols other than HTTP can use the same observation contract through their own inspector.
Example consumer-owned Stack operation:
### Inspector `vue`: Inspect Vue pages and function calls
- Runtime: `nodejs`
- Directory: `.`
- Command: `node` `node_modules/@jskit-ai/architecture-inspector/src/cli.js` `--alias` `@=src` `--calls`
- Timeout seconds: `90`
- Max output bytes: `67108864`
- Preview locator: `node_modules/@jskit-ai/architecture-inspector/src/previewLocator.js`The consumer owns command admission, runtime selection, cancellation, publication and validation against the indexed snapshot. It must retain the last valid snapshot when an inspector fails or source changes.
