feat(extraction): index Vuex/Pinia store actions, mutations, and getters
A Vue store's callable surface — Vuex `actions`/`mutations`/`getters` and Pinia
store actions — lived only as object-literal properties, so the symbols an agent
looks for (`login`, `getSessionList`, `getAuthMenuList`) were never nodes:
`codegraph search`/`codegraph_node` returned "not found" and the agent had to
read the store by hand. This extracts them as function nodes (with their real
bodies + callees), the foundation under any later dispatch-bridge synthesis.
A corpus probe (vue-element-admin, vue2-elm, Geeker-Admin, MallChatWeb) showed
Vue store dispatch is NOT one clean string-keyed shape but ~5; extraction here
covers the three dominant definition forms:
- Vuex MODULE: non-exported `const actions/mutations = {…}` collections
(gated by a ≥2-signal looksLikeVueStoreFile + the object-of-functions shape,
so a Redux file's stray `const actions` is a 0-node no-op).
- Pinia OPTIONS: `defineStore({ actions: {…}, getters: {…} })` — methods of
the actions/mutations/getters properties of a store-factory config.
- Pinia SETUP: `defineStore('id', () => { const foo = …; return {…} })` — the
body-local function consts (findPiniaSetupFn + extractPiniaSetupBody; the
generic body walk doesn't reach nested function scopes). Distinguished from
an inline action map via objectHasInlineFunctions so zustand/SvelteKit
extraction is unchanged.
Validated findable on element-admin (50 fns), Geeker (21), MallChat (68);
0-node no-op on a non-Vue control (uwave-web, unchanged at 4496 nodes). Deferred
(documented in the backlog): vue2-elm's `export default {…}` split-file +
computed-key `commit(CONST)` form (n=1), and the dispatch BRIDGE synthesis
(Vuex string-key + Pinia useStore().action()). Suite green (1610); new
__tests__/vue-store-extraction.test.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
e9f7422223
commit
cc9c2f7420
@@ -56,7 +56,7 @@ Status legend (matches the playbook): ✅ done+validated · 🟡 shipped but und
|
||||
|---|---|---|---|---|
|
||||
| **Name→class registry / command bus** | any (TS/JS first) | object-literal registry `{key: Handler}` + computed-key dispatch `(new) reg[var](…)` | S (fan-out, `object-registry`) | ✅ **SHIPPED v1 (2026-06-20)** — `objectRegistryEdges`. Links each dispatcher fn → each registered handler's callable entry (a class's `execute`/run/handle method — preferring the method chained at the dispatch — or the function value). Precise on **xrengine** (CommandManager, 64 edges, class registry → `.execute`), **Prebid.js** (7: builder/consent/message dispatch, fn registry), **warp-drive** (1). **0 false positives** after: minified-file skip (avg line >200), **depth-aware** entry parse (top-level `key: Ident` only — method-shorthand/nested-object bodies don't leak), callable-only targets (no data `constant`), dynamic-dispatch gate. Handles constructor + field-initializer (`this.` normalized) forms. **Deferred (recall, documented):** assign-then-call (`const h=reg[k]; h()` — warp-drive's main `COMMANDS`), augmentation (`reg[k]=H` — Prebid single-entry), method-shorthand entry recall, and the **cross-file barrel-namespace** variant (trezor `getMethod`: `import * as M; M[method]→new` + computed dynamic import + camel↔Pascal — the hard tier, still 🔬). |
|
||||
| **RTK Query** | TS / Redux Toolkit | `createApi({ endpoints: b => ({ getX: b.query(...) }) })` → generated `useGetXQuery` hook → component; endpoint name ↔ hook name (`getX`↔`useGetXQuery`) is convention | X (extract endpoints) + S (endpoint→hook) | ✅ **SHIPPED (2026-06-20)** — `synthesizedBy:'rtk-query'`. **X:** extraction mints a function node per endpoint (named by its key, spanning the `queryFn`/`query` handler so its calls attribute; both `endpoints: b => ({…})` arrow and `endpoints(b){ return {…} }` method forms; a factory-handler endpoint `queryFn: makeFn(url)` falls back to a bare node spanning the builder call) **and** per generated-hook binding from `export const {…} = api` (carrying the sentinel signature `= RTK Query generated hook`). **S:** `rtkQueryEdges` bridges hook→same-file endpoint by the naming convention (strip `use` + optional `Lazy` + `Query`/`Mutation`, lc head). Component→hook is normal import/call resolution; hook→endpoint surfaces in explore as `dynamic: rtk query`. Validated **100% precision** (hooks == synth edges, **0 cross-file**) on **basetool** (small, 54 edges, both forms + factory fallback), **minusx-metabase** (small, 11), **shapeshift** (large, 13); **0** on the uwave-web control (no `createApi` → a complete no-op, 0 nodes/edges added). Sentinel gate correctly ignores hand-written look-alikes (shapeshift's `useFoxyQuery` is a real custom hook, never bridged). **Deferred:** cross-module `injectEndpoints` where the hook destructuring's RHS isn't the same bare api const (synth requires same-file endpoint). |
|
||||
| **Vuex / Pinia** | Vue | `store.dispatch('ns/action')` / `commit('mutation')` → action/mutation by string key (namespaced) | S (string-keyed, like `event-emitter`) | ⬜ |
|
||||
| **Vuex / Pinia** | Vue | `store.dispatch('ns/action')` / `commit('mutation')` → action/mutation by string key (namespaced); Pinia `useStore().action()` instance call | **X (extract collections) ✅ + S (dispatch bridge) ⬜** | 🟡 **EXTRACTION FOUNDATION SHIPPED (2026-06-20)** — store actions/mutations/getters are now nodes (`codegraph_node login`/`getSessionList` works). Corpus probe found this is **NOT one clean string-keyed shape** — it's ~5: **(1)** Vuex MODULE non-exported `const actions/mutations = {…}` (element-admin), **(2)** Vuex split-file `export default {…}` + computed-key `commit(CONST)` + `mapActions` (vue2-elm), **(3)** Pinia OPTIONS `defineStore({actions:{…}})` (Geeker), **(4)** Pinia SETUP `defineStore('id',()=>{const f=…;return{f}})` body-locals (MallChat), **(5)** Pinia `useStore().action()` instance dispatch. Extraction covers **1, 3, 4** (`extractObjectLiteralFunctions` on `actions`/`mutations`/`getters` collections + a `findPiniaSetupFn`/`extractPiniaSetupBody` for setup locals; `looksLikeVueStoreFile` ≥2-signal gate + the shape gate make it a **0-node no-op on a Redux control** despite the word "actions"). Validated findable on element-admin (50 fns), Geeker (21), MallChat (68); vue2-elm form-2 + computed-key **deferred** (n=1, needs export-default dispatch + const-string resolution). **Next — the dispatch BRIDGE synth (⬜), 2 separate members:** **(a)** Vuex string-key `dispatch('ns/action')`/`commit('M')` → action/mutation node (an `event-emitter`-style string-key clone, + namespace `module/`) — **n=1 in hand (element-admin)**, needs a 2nd string-literal Vuex repo; **(b)** Pinia `useStore().action()` → action (per-file `const s=useXStore()` var-binding → store-file method) — **n=2 in hand (Geeker+MallChat)**. Corpus: `/tmp/cg-vuex-eval/{vue-element-admin,vue2-elm,Geeker-Admin,MallChatWeb}`. |
|
||||
| **NgRx effects** | Angular | `createEffect(() => actions.pipe(ofType(LoginAction), …))` → effect handler; `Store.dispatch(new LoginAction())` → effect by action type/class | S (type/class-keyed) | ⬜ |
|
||||
|
||||
### Tier B — backend command/event/message buses (each needs its own canonical flow + ≥2 repos)
|
||||
|
||||
Reference in New Issue
Block a user