@yottagraph-app/aether-instructions 1.1.21 → 1.1.23

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.
@@ -92,7 +92,47 @@ Key capabilities:
92
92
 
93
93
  ---
94
94
 
95
- ## Step 4: Design the UX
95
+ ## Step 4: Verify Data Availability
96
+
97
+ Before designing UX, verify that the data your app needs actually exists
98
+ in the knowledge graph. This prevents building features around empty data.
99
+
100
+ **If MCP tools are available:**
101
+
102
+ ```
103
+ elemental_get_schema() → list entity types, confirm your target types exist
104
+ elemental_get_entity(entity="Microsoft") → verify entity lookup works with a known entity
105
+ elemental_get_entity(entity="Apple Inc") → try another known entity
106
+ ```
107
+
108
+ If schema calls succeed but entity lookups return "not found," that means
109
+ the entity type exists in the schema but has no data. That's a **data
110
+ issue**, not a broken server. Try different, well-known entity names.
111
+
112
+ **If MCP tools are NOT available, use curl:**
113
+
114
+ ```bash
115
+ # Read credentials from broadchurch.yaml
116
+ GW=$(grep 'url:' broadchurch.yaml | head -1 | sed 's/.*"\(.*\)".*/\1/')
117
+ ORG=$(grep 'org_id:' broadchurch.yaml | sed 's/.*"\(.*\)".*/\1/')
118
+ KEY=$(grep 'qs_api_key:' broadchurch.yaml | sed 's/.*"\(.*\)".*/\1/')
119
+
120
+ # List entity types
121
+ curl -s "$GW/api/qs/$ORG/elemental/metadata/schema" -H "X-Api-Key: $KEY"
122
+
123
+ # Search for a known entity
124
+ curl -s "$GW/api/qs/$ORG/entities/search" \
125
+ -X POST -H "Content-Type: application/json" -H "X-Api-Key: $KEY" \
126
+ -d '{"queries":[{"queryId":1,"query":"Microsoft"}],"maxResults":3,"includeNames":true}'
127
+ ```
128
+
129
+ **Document what you find.** If certain entity types have sparse data, note
130
+ it in your UX plan. Design features around data that actually exists, and
131
+ mark aspirational features (that need more data) as future work.
132
+
133
+ ---
134
+
135
+ ## Step 5: Design the UX
96
136
 
97
137
  Based on the brief, think about the right UX for this specific problem. Do NOT default to a sidebar-with-tabs layout. Consider:
98
138
 
@@ -116,7 +156,7 @@ Present the plan to the user and ask for approval before proceeding.
116
156
 
117
157
  ---
118
158
 
119
- ## Step 5: Build
159
+ ## Step 6: Build
120
160
 
121
161
  Implement the plan:
122
162
 
@@ -128,6 +168,13 @@ Implement the plan:
128
168
  6. Use Vuetify components and the project's dark theme
129
169
  7. Update `DESIGN.md` with what you built
130
170
 
171
+ **Use the pre-built platform utilities:**
172
+
173
+ - `useElementalSchema()` — schema discovery with caching, flavor/PID lookup helpers
174
+ - `buildGatewayUrl()`, `getApiKey()`, `padNeid()` from `utils/elementalHelpers`
175
+ - `searchEntities()`, `getEntityName()` from `utils/elementalHelpers`
176
+ - `useElementalClient()` from `@yottagraph-app/elemental-api/client`
177
+
131
178
  **Follow the project's coding conventions:**
132
179
 
133
180
  - `<script setup lang="ts">` for all Vue components
@@ -136,7 +183,7 @@ Implement the plan:
136
183
 
137
184
  ---
138
185
 
139
- ## Step 6: Verify
186
+ ## Step 7: Verify
140
187
 
141
188
  After building, check dependencies are installed and run a build:
142
189
 
@@ -151,7 +198,7 @@ Then suggest the user run `npm run dev` to preview their app locally.
151
198
 
152
199
  ---
153
200
 
154
- ## Step 7: Next Steps
201
+ ## Step 8: Next Steps
155
202
 
156
203
  > Your app is taking shape! Here's what you can do next:
157
204
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottagraph-app/aether-instructions",
3
- "version": "1.1.21",
3
+ "version": "1.1.23",
4
4
  "description": "Cursor rules, commands, and skills for Aether development",
5
5
  "files": [
6
6
  "rules",
package/rules/api.mdc CHANGED
@@ -28,13 +28,55 @@ For Lovelace **entity types, properties, relationships, and per-source schemas**
28
28
 
29
29
  ## Test Before You Build
30
30
 
31
- **ALWAYS test API calls via curl before writing code that depends on them.**
32
- This is not optional — the Elemental API has response shapes that differ from
33
- what the TypeScript types suggest, and assumptions about nesting, property
34
- formats, and field names will be wrong without testing. This applies doubly
35
- to server-side code, where you can't inspect responses in the browser console.
31
+ **ALWAYS test data access before writing application code.** The Elemental
32
+ API has response shapes that differ from what the TypeScript types suggest,
33
+ and assumptions about nesting, property formats, and field names will be
34
+ wrong without testing.
36
35
 
37
- ### How to test
36
+ ### Step 1: MCP tools (interactive exploration)
37
+
38
+ **If MCP tools appear in your tool list, start here.** MCP handles entity
39
+ resolution, PID lookups, and NEID formatting automatically — use it to
40
+ verify what data exists and how it's structured.
41
+
42
+ ```
43
+ elemental_get_schema() → list all entity types
44
+ elemental_get_schema(flavor="article") → properties for a type
45
+ elemental_get_entity(entity="Apple") → resolve + fetch entity
46
+ elemental_get_related(entity="Apple",
47
+ related_flavor="person") → follow relationships
48
+ ```
49
+
50
+ MCP tells you the correct flavor IDs, property IDs, and data shapes. Use
51
+ these to inform your REST implementation.
52
+
53
+ **Verify MCP is working with known-good queries:**
54
+
55
+ ```
56
+ elemental_get_schema() → should return flavors + properties
57
+ elemental_get_entity(entity="Microsoft") → should resolve to a company
58
+ elemental_get_entity(entity="Apple Inc") → another known entity
59
+ elemental_health() → server health check
60
+ ```
61
+
62
+ **Interpreting MCP errors — do NOT assume the server is broken:**
63
+
64
+ - `entity not found` or 404 in entity lookup → the entity doesn't exist
65
+ in the knowledge graph, not a connectivity problem. Try a different entity.
66
+ - `failed to get property values: 404` → the entity was resolved but has
67
+ no data for those properties. The MCP server is working correctly.
68
+ - Schema calls succeed but entity calls fail → data is sparse for that
69
+ entity type. Try well-known entities (Microsoft, Apple Inc, JPMorgan).
70
+ - If `elemental_health()` fails → actual connectivity problem.
71
+
72
+ **Key insight:** A 404 from an MCP entity/property call means "not found,"
73
+ not "server broken." Always test with known entities before concluding
74
+ the server is down.
75
+
76
+ ### Step 2: curl (verify exact request/response shapes)
77
+
78
+ MCP doesn't cover every REST endpoint (e.g. `/elemental/find` expressions).
79
+ Test those with curl before implementing them in code.
38
80
 
39
81
  The gateway proxy authenticates on your behalf — no Auth0 tokens needed.
40
82
  Read `broadchurch.yaml` for the three values you need:
@@ -48,8 +90,6 @@ Read `broadchurch.yaml` for the three values you need:
48
90
  Build the request URL as `{gateway.url}/api/qs/{tenant.org_id}/{endpoint}`
49
91
  and include the header `X-Api-Key: {gateway.qs_api_key}`.
50
92
 
51
- ### Example calls
52
-
53
93
  ```bash
54
94
  # Variables — read these from broadchurch.yaml
55
95
  GW="https://broadchurch-portal-194773164895.us-central1.run.app"
@@ -62,6 +102,13 @@ curl -s "$GW/api/qs/$ORG/entities/search" \
62
102
  -H "X-Api-Key: $KEY" \
63
103
  -d '{"queries":[{"queryId":1,"query":"Microsoft"}],"maxResults":3}'
64
104
 
105
+ # Test a find expression
106
+ curl -s -X POST "$GW/api/qs/$ORG/elemental/find" \
107
+ -H "X-Api-Key: $KEY" \
108
+ -H "Content-Type: application/x-www-form-urlencoded" \
109
+ --data-urlencode 'expression={"type":"is_type","is_type":{"fid":12}}' \
110
+ --data-urlencode 'limit=5'
111
+
65
112
  # Get entity properties (form-encoded)
66
113
  curl -s -X POST "$GW/api/qs/$ORG/elemental/entities/properties" \
67
114
  -H "X-Api-Key: $KEY" \
@@ -74,6 +121,47 @@ curl -s -X POST "$GW/api/qs/$ORG/elemental/entities/properties" \
74
121
  `application/x-www-form-urlencoded` with JSON-stringified parameter values.
75
122
  All other endpoints accept `application/json`.
76
123
 
124
+ **Interpreting errors:** 400 = expression syntax is wrong. 500 = expression
125
+ is valid but the query failed (wrong PID, unsupported operator for that
126
+ property type). 200 + empty `eids` = query worked but no results match.
127
+ 404 from entity/property endpoints = entity or data doesn't exist (not a
128
+ server error). Always test with known entities (e.g. search for "Microsoft")
129
+ before assuming the API is broken.
130
+
131
+ ### Step 3: Implement with confidence
132
+
133
+ Now write your composable or server route, knowing the exact API shapes.
134
+
135
+ ## Pre-Built Helpers
136
+
137
+ The template includes composables and utilities that handle common
138
+ Elemental API patterns. **Use these instead of writing from scratch:**
139
+
140
+ ### `useElementalSchema()` — Schema Discovery with Caching
141
+
142
+ ```typescript
143
+ const { flavors, properties, flavorByName, pidByName, refresh } = useElementalSchema();
144
+ await refresh(); // fetches once, then cached
145
+ const articleFid = flavorByName('article'); // → number | null
146
+ const namePid = pidByName('name'); // → typically 8
147
+ ```
148
+
149
+ Handles the dual response shapes (`res.schema.flavors` vs `res.flavors`)
150
+ and the `fid`/`findex` naming inconsistency automatically.
151
+
152
+ ### `utils/elementalHelpers` — Gateway URL Helpers
153
+
154
+ ```typescript
155
+ import { buildGatewayUrl, getApiKey, padNeid, searchEntities, getEntityName } from '~/utils/elementalHelpers';
156
+
157
+ const url = buildGatewayUrl('entities/search'); // full gateway URL
158
+ const key = getApiKey(); // from runtimeConfig
159
+ const neid = padNeid('4926132345040704022'); // → "04926132345040704022"
160
+
161
+ const results = await searchEntities('Microsoft'); // batch name search
162
+ const name = await getEntityName(neid); // display name lookup
163
+ ```
164
+
77
165
  ## Client Usage
78
166
 
79
167
  All API calls go through `useElementalClient()` from `@yottagraph-app/elemental-api/client`.
@@ -163,26 +251,46 @@ knowledge of what's in the graph.
163
251
 
164
252
  ## API Gotchas
165
253
 
166
- ### `getSchema()` response is nested WILL crash if you don't handle it
254
+ ### `getSchema()` response structure differs by endpoint
255
+
256
+ There are two schema endpoints with **different response shapes**:
167
257
 
168
- The generated TypeScript types suggest `response.properties` and
169
- `response.flavors` exist at the top level. **They don't.** The API nests
170
- them under `response.schema`. This mismatch between types and reality
171
- causes `Cannot read properties of undefined` every time.
258
+ | Endpoint | Flavors at | Flavor ID field | Detail level |
259
+ |----------|-----------|-----------------|--------------|
260
+ | `GET /schema` | top-level (`res.flavors`) | `findex` | Rich (display names, units, domains) |
261
+ | `GET /elemental/metadata/schema` | nested (`res.schema.flavors`) | `fid` | Basic (name + type only) |
262
+
263
+ The TypeScript client's `getSchema()` calls `/elemental/metadata/schema`,
264
+ so the response nests data under `.schema`. The generated types may suggest
265
+ top-level access, but it won't work at runtime.
172
266
 
173
267
  ```typescript
174
- // WRONG — will crash at runtime despite TypeScript compiling fine:
268
+ // WRONG — will crash (data is nested under .schema):
175
269
  const res = await client.getSchema();
176
270
  const props = res.properties; // undefined!
177
271
 
178
- // CORRECT — always access through .schema:
272
+ // CORRECT — always use fallback to handle both shapes:
179
273
  const res = await client.getSchema();
180
274
  const properties = res.schema?.properties ?? (res as any).properties ?? [];
181
275
  const flavors = res.schema?.flavors ?? (res as any).flavors ?? [];
182
276
  ```
183
277
 
184
- The `(res as any).properties` fallback is there in case the API is ever
185
- fixed to match the types. Use this pattern every time.
278
+ ### Flavor ID field: `fid` vs `findex`
279
+
280
+ The flavor identifier has **different field names** depending on the endpoint:
281
+ `GET /schema` returns `findex`, `/elemental/metadata/schema` returns `fid`.
282
+ Same value, different key. Always use a fallback:
283
+
284
+ ```typescript
285
+ const articleFlavor = flavors.find(f => f.name === 'article');
286
+ const articleFid = articleFlavor?.fid ?? articleFlavor?.findex ?? null;
287
+
288
+ // When building a FID lookup map:
289
+ const fidMap = new Map(flavors.map(f => [f.fid ?? f.findex, f.name]));
290
+ ```
291
+
292
+ The `is_type` expression in `/elemental/find` always uses the `fid` key
293
+ regardless of which schema endpoint provided the value.
186
294
 
187
295
  ### Relationship property values need zero-padding to form valid NEIDs
188
296
 
@@ -273,6 +381,23 @@ const docNeids = (res.values ?? []).map((v) => String(v.value).padStart(20, '0')
273
381
 
274
382
  See the **cookbook** rule for a full "Get filings for a company" recipe.
275
383
 
384
+ ### Expression language pitfalls
385
+
386
+ These mistakes come up repeatedly when building `/elemental/find` queries:
387
+
388
+ - **Entity type filtering**: Use `is_type` (not `comparison` with pid=0).
389
+ `comparison` requires `pid != 0`.
390
+ - **`string_like` is name-only**: Only works on the name property (PID 8).
391
+ Use `eq` for exact matches on other string properties.
392
+ - **Boolean combinators**: Use `{"type": "and", "and": [...]}` — not
393
+ `conjunction` or any other name.
394
+ - **`lt`/`gt` are numeric-only**: Only work on `data_int` and `data_float`
395
+ properties.
396
+ - **`regex` is not implemented**: Will return an error.
397
+
398
+ Read the "Common Mistakes" section in the **elemental-api skill** (`find.md`)
399
+ for examples of each.
400
+
276
401
  ### Entity Search
277
402
 
278
403
  Use `client.findEntities()` (`POST /elemental/find`) for entity search.
@@ -327,17 +452,13 @@ const response = await getArticle(artid);
327
452
  if (response.status === 404) { /* handle not found */ }
328
453
  ```
329
454
 
330
- ## Lovelace MCP Servers (Optional)
455
+ ## Lovelace MCP Servers
331
456
 
332
- Four MCP servers **may** be configured in `.cursor/mcp.json` for interactive
333
- data exploration. They are NOT requiredthe REST client
334
- (`useElementalClient()`) and skill docs cover the same data and are the
335
- primary path for building features.
336
-
337
- **Before referencing MCP tools, check your available tool list.** If tools
338
- like `elemental_get_schema` don't appear in your MCP server list, the
339
- servers aren't connected — just use the REST client and skill docs instead.
340
- Do not report this as a problem; it's a normal configuration state.
457
+ Four MCP servers may be configured in `.cursor/mcp.json` for interactive
458
+ data exploration. **Check your tool list**if tools like
459
+ `elemental_get_schema` appear, use them as your primary testing and
460
+ exploration interface before writing code. If they don't appear, the
461
+ servers aren't connected; use curl and the skill docs instead.
341
462
 
342
463
  | Server | What it provides |
343
464
  |---|---|
@@ -346,6 +467,23 @@ Do not report this as a problem; it's a normal configuration state.
346
467
  | `lovelace-wiki` | Wikipedia entity enrichment |
347
468
  | `lovelace-polymarket` | Prediction market data |
348
469
 
470
+ ### MCP Tool Quick Reference
471
+
472
+ | Tool | Purpose | Use to verify... |
473
+ |---|---|---|
474
+ | `elemental_get_schema` | Discover entity types (flavors), properties, and relationships | Flavor IDs, property IDs, data types |
475
+ | `elemental_get_entity` | Look up entity by name or NEID; returns properties | Entity resolution, property shapes |
476
+ | `elemental_get_related` | Related entities with type/relationship filters | Relationship types and traversal |
477
+ | `elemental_get_relationships` | Relationship types and counts between two entities | Edge types between specific entities |
478
+ | `elemental_graph_neighborhood` | Most influential neighbors of an entity | Graph connectivity |
479
+ | `elemental_graph_sentiment` | Sentiment analysis from news articles | Sentiment data availability |
480
+ | `elemental_get_events` | Events for an entity or by search query | Event categories and shapes |
481
+ | `elemental_health` | Health check | Server connectivity |
482
+
483
+ MCP tools handle entity resolution, PID lookups, and NEID formatting
484
+ automatically. Use them to discover IDs and verify data exists before
485
+ building REST-based features with `useElementalClient()`.
486
+
349
487
  ### Setup
350
488
 
351
489
  `.cursor/mcp.json` is auto-generated by `init-project.js`. If it's missing,
@@ -353,25 +491,3 @@ run `node init-project.js --local` to regenerate it. For provisioned projects,
353
491
  the servers route through the Portal Gateway proxy (no credentials needed).
354
492
  For local development without a gateway, the servers require an
355
493
  `AUTH0_M2M_DEV_TOKEN` environment variable.
356
-
357
- These servers are also accessible from the browser through the Portal
358
- Gateway, so you can build MCP tool exploration UIs if needed.
359
-
360
- ### When MCP Servers Are Available
361
-
362
- If the MCP tools appear in your tool list, you can use them for interactive
363
- exploration alongside the REST client:
364
-
365
- | Tool | Purpose |
366
- |---|---|
367
- | `elemental_get_schema` | Discover entity types (flavors), properties, and relationships |
368
- | `elemental_get_entity` | Look up entity by name or NEID; returns properties |
369
- | `elemental_get_related` | Related entities with type/relationship filters |
370
- | `elemental_get_relationships` | Relationship types and counts between two entities |
371
- | `elemental_graph_neighborhood` | Most influential neighbors of an entity |
372
- | `elemental_graph_sentiment` | Sentiment analysis from news articles |
373
- | `elemental_get_events` | Events for an entity or by search query |
374
- | `elemental_health` | Health check |
375
-
376
- MCP is convenient for schema discovery and entity resolution during planning.
377
- For building UI features, always use the REST client (`useElementalClient()`).
@@ -388,7 +388,169 @@ Two-column layout with selectable list and detail panel.
388
388
  </script>
389
389
  ```
390
390
 
391
- ## 7. Get Filings for a Company
391
+ ## 7. News Feed Recent Articles with Sentiment
392
+
393
+ Fetch recent articles from the knowledge graph. Uses `useElementalSchema()`
394
+ for runtime flavor/PID discovery and `buildGatewayUrl()` for gateway access.
395
+
396
+ ```vue
397
+ <template>
398
+ <div class="d-flex flex-column fill-height pa-4">
399
+ <h1 class="text-h5 mb-4">Recent News</h1>
400
+ <v-alert v-if="error" type="error" variant="tonal" class="mb-4" closable>
401
+ {{ error }}
402
+ </v-alert>
403
+ <v-progress-linear v-if="loading" indeterminate class="mb-4" />
404
+ <v-list v-if="articles.length" lines="three">
405
+ <v-list-item v-for="a in articles" :key="a.neid">
406
+ <template #title>
407
+ <span>{{ a.name || a.neid }}</span>
408
+ <v-chip
409
+ v-if="a.sentiment"
410
+ size="x-small"
411
+ class="ml-2"
412
+ :color="a.sentiment > 0 ? 'success' : a.sentiment < 0 ? 'error' : 'grey'"
413
+ >
414
+ {{ a.sentiment > 0 ? 'Bullish' : a.sentiment < 0 ? 'Bearish' : 'Neutral' }}
415
+ </v-chip>
416
+ </template>
417
+ <template #subtitle>{{ a.neid }}</template>
418
+ </v-list-item>
419
+ </v-list>
420
+ <v-empty-state
421
+ v-else-if="!loading"
422
+ headline="No articles found"
423
+ icon="mdi-newspaper-variant-outline"
424
+ />
425
+ </div>
426
+ </template>
427
+
428
+ <script setup lang="ts">
429
+ import { useElementalClient } from '@yottagraph-app/elemental-api/client';
430
+ import { padNeid } from '~/utils/elementalHelpers';
431
+
432
+ const client = useElementalClient();
433
+ const { flavorByName, pidByName, refresh: loadSchema } = useElementalSchema();
434
+
435
+ const articles = ref<{ neid: string; name: string; sentiment: number | null }[]>([]);
436
+ const loading = ref(false);
437
+ const error = ref<string | null>(null);
438
+
439
+ onMounted(async () => {
440
+ loading.value = true;
441
+ try {
442
+ await loadSchema();
443
+ const articleFid = flavorByName('article');
444
+ if (!articleFid) {
445
+ error.value = 'Article entity type not found in schema';
446
+ return;
447
+ }
448
+
449
+ const res = await client.findEntities({
450
+ expression: JSON.stringify({ type: 'is_type', is_type: { fid: articleFid } }),
451
+ limit: 20,
452
+ });
453
+ const neids: string[] = (res as any).eids ?? [];
454
+
455
+ if (!neids.length) {
456
+ return;
457
+ }
458
+
459
+ const namePid = pidByName('name');
460
+ const sentimentPid = pidByName('sentiment');
461
+ const pids = [namePid, sentimentPid].filter((p): p is number => p !== null);
462
+
463
+ const props = await client.getPropertyValues({
464
+ eids: JSON.stringify(neids),
465
+ pids: JSON.stringify(pids),
466
+ });
467
+
468
+ const valueMap = new Map<string, Record<number, any>>();
469
+ for (const v of (props as any).values ?? []) {
470
+ const eid = padNeid(v.eid ?? v.entity_id ?? '');
471
+ if (!valueMap.has(eid)) valueMap.set(eid, {});
472
+ valueMap.get(eid)![v.pid] = v.value;
473
+ }
474
+
475
+ articles.value = neids.map((neid) => {
476
+ const vals = valueMap.get(neid) ?? {};
477
+ return {
478
+ neid,
479
+ name: namePid ? (vals[namePid] as string) ?? neid : neid,
480
+ sentiment: sentimentPid ? (vals[sentimentPid] as number) ?? null : null,
481
+ };
482
+ });
483
+ } catch (e: any) {
484
+ error.value = e.message || 'Failed to load articles';
485
+ } finally {
486
+ loading.value = false;
487
+ }
488
+ });
489
+ </script>
490
+ ```
491
+
492
+ ## 8. Entity Search with Gateway Helpers
493
+
494
+ Simpler version of recipe #1 using the pre-built `searchEntities()` helper.
495
+
496
+ ```vue
497
+ <template>
498
+ <div class="d-flex flex-column fill-height pa-4">
499
+ <h1 class="text-h5 mb-4">Entity Search</h1>
500
+ <v-text-field
501
+ v-model="query"
502
+ label="Search entities"
503
+ prepend-inner-icon="mdi-magnify"
504
+ variant="outlined"
505
+ @keyup.enter="search"
506
+ :loading="loading"
507
+ />
508
+ <v-alert v-if="error" type="error" variant="tonal" class="mt-2" closable>
509
+ {{ error }}
510
+ </v-alert>
511
+ <v-list v-if="results.length" class="mt-4">
512
+ <v-list-item
513
+ v-for="r in results"
514
+ :key="r.neid"
515
+ :title="r.name"
516
+ :subtitle="r.neid"
517
+ />
518
+ </v-list>
519
+ <v-empty-state
520
+ v-else-if="searched && !loading"
521
+ headline="No results"
522
+ icon="mdi-magnify-remove-outline"
523
+ />
524
+ </div>
525
+ </template>
526
+
527
+ <script setup lang="ts">
528
+ import { searchEntities } from '~/utils/elementalHelpers';
529
+
530
+ const query = ref('');
531
+ const results = ref<{ neid: string; name: string }[]>([]);
532
+ const loading = ref(false);
533
+ const error = ref<string | null>(null);
534
+ const searched = ref(false);
535
+
536
+ async function search() {
537
+ if (!query.value.trim()) return;
538
+ loading.value = true;
539
+ error.value = null;
540
+ searched.value = true;
541
+ try {
542
+ results.value = await searchEntities(query.value.trim());
543
+ } catch (e: any) {
544
+ error.value = e.message || 'Search failed';
545
+ results.value = [];
546
+ } finally {
547
+ loading.value = false;
548
+ }
549
+ }
550
+ </script>
551
+ ```
552
+
553
+ ## 9. Get Filings for a Company
392
554
 
393
555
  Fetch Edgar filings (or any relationship-linked documents) for an organization.
394
556
  Uses `$fetch` for the initial entity search because `POST /entities/search`