@sungen/driver-api 3.1.2-beta.117 → 3.1.2-beta.119

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sungen/driver-api",
3
- "version": "3.1.2-beta.117",
3
+ "version": "3.1.2-beta.119",
4
4
  "description": "Sungen API capability — the @api annotation, API catalog, OpenAPI import + `sungen api import`, and the specs/api.ts runtime template. Plugs into @sun-asterisk/sungen via the capability SPI.",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -13,7 +13,7 @@
13
13
  "author": "eqe team (engineer & quality) — Sun Asterisk",
14
14
  "license": "MIT",
15
15
  "dependencies": {
16
- "@sun-asterisk/sungen": "3.1.2-beta.117",
16
+ "@sun-asterisk/sungen": "3.1.2-beta.119",
17
17
  "commander": "^14.0.2",
18
18
  "yaml": "^2.8.2"
19
19
  },
@@ -0,0 +1,52 @@
1
+ /**
2
+ * API discovery + context-mapping (AO-3) — the `api` capability's Discover → Contextualize hooks.
3
+ *
4
+ * Discovery reads the reviewed catalog (apis.yaml) + notes the spec source; context-mapping turns
5
+ * the endpoints into generation units by method-profile: every endpoint → a `matrix` unit (contract
6
+ * + error), each mutating endpoint → also an `async` unit (idempotency), and an api flow → a `flow`
7
+ * unit (the ordered journey). The orchestration loop (`sungen context` → `/sungen:design`) consumes
8
+ * these to know WHAT to author before writing scenarios. Type-only on core.
9
+ */
10
+ import type { Context, DiscoveryProvider, ContextMapper, GenerationUnit } from '@sun-asterisk/sungen';
11
+ import * as fs from 'node:fs';
12
+ import * as path from 'node:path';
13
+ import { lintApiCatalog } from './api-catalog';
14
+
15
+ interface EndpointFact { name: string; method: string; path: string; datasource?: string; expectStatus?: number | string }
16
+
17
+ const MUTATING = /^(POST|PUT|PATCH|DELETE)$/;
18
+
19
+ export const apiDiscovery: DiscoveryProvider = {
20
+ appliesTo: (t) => t.kind === 'api',
21
+ discover: async (target, scope) => {
22
+ const cwd = (scope as { cwd?: string } | undefined)?.cwd ?? process.cwd();
23
+ const unitId = target.id; // `api/<area>` | `api/flows/<flow>`
24
+ let endpoints: EndpointFact[] = [];
25
+ try {
26
+ endpoints = lintApiCatalog(unitId, null, cwd).entries.map((e) => ({
27
+ name: e.name, method: e.method, path: e.path, datasource: e.datasource, expectStatus: e.expect?.status,
28
+ }));
29
+ } catch { /* no catalog → the verification gate reports it */ }
30
+
31
+ const sources: Record<string, unknown> = {};
32
+ const specPath = path.join(cwd, 'qa', unitId, 'requirements', 'spec.md');
33
+ if (fs.existsSync(specPath)) sources.spec = path.relative(cwd, specPath);
34
+
35
+ return { target: { ...target, capability: 'api' }, sources, facts: { endpoints } };
36
+ },
37
+ };
38
+
39
+ export const apiContextMapper: ContextMapper = {
40
+ decompose: (ctx: Context): GenerationUnit[] => {
41
+ const endpoints = ((ctx.facts ?? {}) as { endpoints?: EndpointFact[] }).endpoints ?? [];
42
+ const units: GenerationUnit[] = [];
43
+ const isFlow = typeof ctx.target.id === 'string' && ctx.target.id.includes('/flows/');
44
+ if (isFlow) units.push({ mode: 'flow', targetSlice: ctx.target }); // the ordered journey is one unit
45
+ for (const e of endpoints) {
46
+ units.push({ mode: 'matrix', targetSlice: e }); // contract + error matrix (70%)
47
+ if (MUTATING.test(e.method)) units.push({ mode: 'async', targetSlice: e }); // idempotency/concurrency (10%)
48
+ }
49
+ if (!units.length) units.push({ mode: 'matrix', targetSlice: ctx.target });
50
+ return units;
51
+ },
52
+ };
package/src/index.ts CHANGED
@@ -20,6 +20,7 @@ import type { CapabilityRegistry, Sensor, GateInput } from '@sun-asterisk/sungen
20
20
  import { resolveApi, apiParamNames, lintApiCatalog } from './api-catalog';
21
21
  import { registerApiCommand } from './cli-import';
22
22
  import { apiViewpoints, apiGateProvider } from './api-harness';
23
+ import { apiDiscovery, apiContextMapper } from './api-context';
23
24
 
24
25
  /**
25
26
  * Parse `name(a={{x}},b="lit",c=3)` annotation overrides → JS expressions. Inlined from core
@@ -187,6 +188,8 @@ export function register(registry: CapabilityRegistry): void {
187
188
  sensors: [apiVerificationSensor, apiViewpointSensor],
188
189
  viewpoints: apiViewpoints as () => unknown, // AO-2: API viewpoint catalog
189
190
  gateProvider: apiGateProvider as (i: unknown) => unknown, // AO-2: API coverage + businessDepth
191
+ discovery: apiDiscovery, // AO-3: catalog/spec → Context slice
192
+ contextMapper: apiContextMapper, // AO-3: endpoints → matrix/async/flow generation units
190
193
  cliCommands: [registerApiCommand as (program: unknown) => void], // `sungen api import <openapi>`
191
194
  });
192
195
  }