@nospt/backstage-plugin-apigee-backend 0.1.0

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/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @nospt/backstage-plugin-apigee-backend
2
+
3
+ Backend package for the bootstrap stage of the Backstage Apigee plugin.
4
+
5
+ ## Included
6
+
7
+ - New Backend System plugin registration
8
+ - Minimal router mounted at `/api/apigee`
9
+ - Placeholder health endpoint at `/api/apigee/health`
10
+
11
+ ## Development
12
+
13
+ ```bash
14
+ cd plugins/apigee-backend
15
+ yarn start
16
+ yarn test
17
+ yarn lint
18
+ yarn build
19
+ ```
20
+
21
+ ## Repository Stitching
22
+
23
+ A Backstage `CatalogProcessor` (`ApigeeStitchingProcessor`) links a backend
24
+ `Component` to its proxy `API` entity by emitting the native `providesApi` /
25
+ `apiProvidedBy` relations. This surfaces the standard **"Provided APIs"** card on
26
+ the component page and the **"Providers"** card on the API page — with zero custom
27
+ UI. A repository opts in by adding `nos.pt/apigee-*` annotations to its
28
+ `catalog-info.yaml`.
29
+
30
+ ### Annotation styles
31
+
32
+ **Direct mapping (active in this release):**
33
+
34
+ ```yaml
35
+ apiVersion: backstage.io/v1alpha1
36
+ kind: Component
37
+ metadata:
38
+ name: orders-service
39
+ annotations:
40
+ nos.pt/apigee-api-name: my-proxy
41
+ nos.pt/apigee-project-id: apigee-x-public-p-123
42
+ spec:
43
+ type: service
44
+ owner: team-orders
45
+ lifecycle: production
46
+ ```
47
+
48
+ **TargetServer-based mapping (documented; resolution planned, not yet active):**
49
+
50
+ ```yaml
51
+ metadata:
52
+ annotations:
53
+ nos.pt/apigee-targetserver-hostname: backend.nos.pt
54
+ nos.pt/apigee-targetserver-port: "443"
55
+ ```
56
+
57
+ ### Precedence rule
58
+
59
+ When both styles are present, the **TargetServer-based mapping is the preferred**
60
+ resolution path (STITCH-02). The direct API-name mapping is the **fallback**, used
61
+ when no TargetServer annotation is present or when TargetServer resolution is
62
+ ambiguous (for example, a generic load balancer shared by many proxies).
63
+
64
+ > **In this release only the direct-mapping path is implemented.** TargetServer
65
+ > resolution is planned but not yet active — no TargetServer data is captured by the
66
+ > entity provider yet (see `04-CONTEXT` D-01). The processor is structured around a
67
+ > resolution-strategy seam so the TargetServer branch slots in without rework.
68
+
69
+ ### Apigee → Backstage mapping
70
+
71
+ | Apigee concept | Backstage entity |
72
+ |----------------|------------------|
73
+ | API proxy (implementation) | `kind: Component`, `spec.type: api-proxy` |
74
+ | API proxy (contract) | `kind: API`, `spec.type` from API Hub (default `other`) |
75
+ | Sharedflow | `kind: Component`, `spec.type: library` |
76
+ | Backend repo component ↔ proxy API | `providesApi` / `apiProvidedBy` relation |
77
+
78
+ ### Resolution detail
79
+
80
+ The target API entity ref is deterministic:
81
+ `api:default/<slugify(nos.pt/apigee-api-name)>`. The processor reads the annotations,
82
+ synthesizes the ref, and emits the relation pair — no live catalog lookup is needed.
83
+ A partial annotation set (e.g. `nos.pt/apigee-api-name` without
84
+ `nos.pt/apigee-project-id`) or an empty/unresolvable value is skipped with a warning
85
+ (warn-and-skip), so no malformed relations are emitted.
86
+
87
+ ### Example fixture
88
+
89
+ See [`examples/components.yaml`](./examples/components.yaml) for ready-to-register
90
+ sample `Component` entities exercising both annotation styles — useful for local
91
+ smoke testing of the stitching processor.
package/config.d.ts ADDED
@@ -0,0 +1,75 @@
1
+ export interface Config {
2
+ /** Apigee catalog module configuration */
3
+ apigee?: {
4
+ /**
5
+ * List of Apigee X organisations to sync.
6
+ * At least one entry is required; the module fails fast at startup if missing.
7
+ * @visibility backend
8
+ */
9
+ orgs: Array<{
10
+ /** Human-readable label for this org */
11
+ name: string;
12
+ /** GCP project ID that owns this Apigee X organisation */
13
+ projectId: string;
14
+ /**
15
+ * Optional list of environments to include (e.g. ["prod", "staging"]).
16
+ * If omitted, all environments are synced.
17
+ * @visibility backend
18
+ */
19
+ environments?: string[];
20
+ /**
21
+ * Optional GitHub organisation slug used to derive GitHub repo links
22
+ * when API Hub does not supply a repositoryUri (FR17).
23
+ * @visibility backend
24
+ */
25
+ githubOrgSlug?: string;
26
+ /**
27
+ * Explicit proxy-name -> API Hub apiId overrides for proxies whose slug
28
+ * does not match their API Hub apiId (D-01).
29
+ * @visibility backend
30
+ */
31
+ apiHubMappings?: { [proxyName: string]: string };
32
+ }>;
33
+ /**
34
+ * Catalog sync interval in minutes. Default: 60.
35
+ * @visibility backend
36
+ */
37
+ syncIntervalMinutes?: number;
38
+ /**
39
+ * Default spec.owner for API entities whose API Hub ownerTeam is absent.
40
+ * Must be a Backstage entity reference, e.g. "group:default/platform-team".
41
+ * @visibility backend
42
+ */
43
+ defaultOwner?: string;
44
+ /**
45
+ * Maximum inline spec size in bytes; specs larger than this are linked via
46
+ * apigee.com/spec-url instead of embedded. Default 512000 (500 KB).
47
+ * @visibility backend
48
+ */
49
+ specMaxBytes?: number;
50
+ /**
51
+ * API Hub location (GCP region) to query. Defaults to "global".
52
+ * Use this when your API Hub instance is regional, e.g. "europe-west1".
53
+ * @visibility backend
54
+ */
55
+ apiHubLocation?: string;
56
+ /**
57
+ * Optional custom schedule for the catalog sync task.
58
+ * When provided, overrides syncIntervalMinutes.
59
+ * @visibility backend
60
+ */
61
+ schedule?: {
62
+ /** How often to run the sync. */
63
+ frequency?: {
64
+ cron?: string;
65
+ minutes?: number;
66
+ hours?: number;
67
+ seconds?: number;
68
+ };
69
+ /** Maximum time allowed for a single sync run. */
70
+ timeout?: { minutes?: number; seconds?: number };
71
+ /** Delay before the first run after startup. */
72
+ initialDelay?: { minutes?: number; seconds?: number };
73
+ };
74
+ };
75
+ }
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var plugin = require('./plugin.cjs.js');
6
+ var module$1 = require('./module.cjs.js');
7
+ var apigeeClient = require('./lib/apigee-client.cjs.js');
8
+ var apiHubClient = require('./lib/api-hub-client.cjs.js');
9
+ var entityProvider = require('./lib/entity-provider.cjs.js');
10
+ var sharedflowClient = require('./lib/sharedflow-client.cjs.js');
11
+ var apigeeStitchingProcessor = require('./lib/apigee-stitching-processor.cjs.js');
12
+
13
+
14
+
15
+ exports.default = plugin.apigeePlugin;
16
+ exports.catalogModuleApigee = module$1.catalogModuleApigee;
17
+ exports.ApigeeClient = apigeeClient.ApigeeClient;
18
+ exports.ApiHubClient = apiHubClient.ApiHubClient;
19
+ exports.ApigeeEntityProvider = entityProvider.ApigeeEntityProvider;
20
+ exports.SharedflowClient = sharedflowClient.SharedflowClient;
21
+ exports.ApigeeStitchingProcessor = apigeeStitchingProcessor.ApigeeStitchingProcessor;
22
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,431 @@
1
+ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
+ import { SchedulerServiceTaskRunner, LoggerService } from '@backstage/backend-plugin-api';
3
+ import { GoogleAuth } from 'google-auth-library';
4
+ import { EntityProvider, EntityProviderConnection, CatalogProcessor, LocationSpec, CatalogProcessorEmit } from '@backstage/plugin-catalog-node';
5
+ import { Entity } from '@backstage/catalog-model';
6
+
7
+ declare const apigeePlugin: _backstage_backend_plugin_api.BackendFeature;
8
+
9
+ /**
10
+ * Backstage catalog backend module for Apigee API discovery.
11
+ * Plugs into the catalog backend and registers Apigee proxies + API Hub
12
+ * definitions as Backstage API entities.
13
+ */
14
+ declare const catalogModuleApigee: _backstage_backend_plugin_api.BackendFeature;
15
+
16
+ /** Represents a single Apigee API proxy entry from GET /organizations/{org}/apis */
17
+ interface ApigeeProxy {
18
+ /** Proxy name as registered in Apigee (may contain dots/underscores — slugify before use as metadata.name) */
19
+ name: string;
20
+ /** Array of revision numbers (strings), most recent last */
21
+ revision?: string[];
22
+ /** ISO 8601 creation timestamp */
23
+ createdAt?: string;
24
+ /** ISO 8601 last-modified timestamp */
25
+ lastModifiedAt?: string;
26
+ }
27
+ /** Represents one environment deployment from GET /organizations/{org}/apis/{proxy}/deployments */
28
+ interface ApigeeDeployment {
29
+ /** Environment name (e.g. "prod", "staging") */
30
+ environment: string;
31
+ /** Deployed revision number */
32
+ apiRevision?: string;
33
+ /** Deployment state: READY | PROGRESSING | ERROR */
34
+ deploymentStatus?: string;
35
+ }
36
+ /**
37
+ * Represents a single proxy revision detail from
38
+ * GET /organizations/{org}/apis/{proxy}/revisions/{rev}
39
+ */
40
+ interface ApigeeProxyRevision {
41
+ name: string;
42
+ revision?: string;
43
+ /** Base paths declared in the proxy bundle (used for apigee.com/base-path annotation) */
44
+ basepaths?: string[];
45
+ description?: string;
46
+ /** Resource files attached to this revision */
47
+ resourceFiles?: {
48
+ resourceFile?: Array<{
49
+ name: string;
50
+ type: string;
51
+ }>;
52
+ };
53
+ }
54
+ /**
55
+ * Read-only HTTP client for the Apigee Management API v1.
56
+ *
57
+ * Auth: uses GCP Application Default Credentials via the injected GoogleAuth
58
+ * instance. All calls are GET-only (AUTH-02: read-only IAM roles).
59
+ *
60
+ * Usage:
61
+ * ```typescript
62
+ * const auth = new GoogleAuth({ scopes: ['https://www.googleapis.com/auth/cloud-platform'] });
63
+ * const client = new ApigeeClient(auth);
64
+ * const proxies = await client.listProxies('my-apigee-org');
65
+ * ```
66
+ */
67
+ declare class ApigeeClient {
68
+ private readonly auth;
69
+ constructor(auth: GoogleAuth);
70
+ /**
71
+ * Lists all API proxies in the given Apigee organisation.
72
+ * Calls: GET /organizations/{org}/apis
73
+ */
74
+ listProxies(org: string): Promise<ApigeeProxy[]>;
75
+ /**
76
+ * Lists all environment deployments for a given proxy.
77
+ * Calls: GET /organizations/{org}/apis/{proxy}/deployments
78
+ */
79
+ listDeployments(org: string, proxy: string): Promise<ApigeeDeployment[]>;
80
+ /**
81
+ * Fetches full detail for a specific proxy revision.
82
+ * Calls: GET /organizations/{org}/apis/{proxy}/revisions/{rev}
83
+ */
84
+ getProxyRevision(org: string, proxy: string, rev: string): Promise<ApigeeProxyRevision>;
85
+ }
86
+
87
+ /**
88
+ * Represents a single API definition from Cloud API Hub.
89
+ * GET /projects/{p}/locations/{l}/apis
90
+ */
91
+ interface ApiHubApi {
92
+ /** Full resource name: projects/{p}/locations/{l}/apis/{id} */
93
+ name: string;
94
+ /** Human-readable display name */
95
+ displayName?: string;
96
+ /** Description from API Hub */
97
+ description?: string;
98
+ /** API Hub lifecycle state: DRAFT | ACTIVE | DEPRECATED | ARCHIVED */
99
+ businessUnit?: string;
100
+ /** Owner team name (maps to spec.owner in Backstage) */
101
+ owner?: {
102
+ displayName?: string;
103
+ email?: string;
104
+ };
105
+ /**
106
+ * GitHub / source repo URI from API Hub metadata.
107
+ * Maps to metadata.links in Backstage (FR17 / META-10).
108
+ */
109
+ sourceMetadata?: Array<{
110
+ sourceType?: string;
111
+ value?: string;
112
+ }>;
113
+ /** Array of category/tag IDs applied to this API in API Hub (META-09) */
114
+ categories?: string[];
115
+ /** API Hub catalog type (e.g. "openapi", "mcp") — maps to spec.type in Backstage */
116
+ apiType?: string;
117
+ /** ISO 8601 create/update timestamps */
118
+ createTime?: string;
119
+ updateTime?: string;
120
+ }
121
+ /**
122
+ * Represents one version of an API Hub API.
123
+ * GET /projects/{p}/locations/{l}/apis/{apiId}/versions
124
+ */
125
+ interface ApiHubVersion {
126
+ /** Full resource name: .../apis/{id}/versions/{vid} */
127
+ name: string;
128
+ displayName?: string;
129
+ description?: string;
130
+ /** Version state: DRAFT | ACTIVE | DEPRECATED | ARCHIVED */
131
+ lifecycle?: {
132
+ stage?: string;
133
+ };
134
+ /** Spec IDs attached to this version */
135
+ specIds?: string[];
136
+ createTime?: string;
137
+ updateTime?: string;
138
+ }
139
+ /**
140
+ * Represents a spec (OpenAPI document) attached to a version.
141
+ * GET /projects/{p}/locations/{l}/apis/{apiId}/versions/{versionId}/specs/{specId}
142
+ */
143
+ interface ApiHubSpec {
144
+ /** Full resource name */
145
+ name: string;
146
+ /** MIME type (e.g. "application/json", "application/yaml") */
147
+ mimeType?: string;
148
+ /** File name (e.g. "openapi.yaml") */
149
+ filename?: string;
150
+ /** Hash of spec contents for deduplication */
151
+ hash?: string;
152
+ /** Size in bytes — used by spec size guard in Phase 3 */
153
+ sizeBytes?: number;
154
+ createTime?: string;
155
+ updateTime?: string;
156
+ }
157
+ /**
158
+ * Raw spec contents response from the :contents sub-resource.
159
+ * `contents` is base64-encoded (Cloud API Hub ApiSpecContents resource).
160
+ */
161
+ interface ApiHubSpecContents {
162
+ mimeType?: string;
163
+ /** Base64-encoded spec file content */
164
+ contents?: string;
165
+ }
166
+ /**
167
+ * Read-only HTTP client for the Cloud API Hub REST v1 API.
168
+ *
169
+ * Mirrors ApigeeClient exactly (D-04): same constructor pattern, same
170
+ * fetchWithRetry delegation, same encodeURIComponent usage.
171
+ *
172
+ * Auth: uses the same GCP Application Default Credentials GoogleAuth instance
173
+ * that is shared with ApigeeClient (D-03).
174
+ *
175
+ * Usage:
176
+ * ```typescript
177
+ * const auth = new GoogleAuth({ scopes: ['https://www.googleapis.com/auth/cloud-platform'] });
178
+ * const client = new ApiHubClient(auth, 'my-gcp-project');
179
+ * const apis = await client.listApis();
180
+ * ```
181
+ */
182
+ declare class ApiHubClient {
183
+ private readonly auth;
184
+ private readonly projectId;
185
+ private readonly location;
186
+ /**
187
+ * @param auth - Shared GoogleAuth instance (D-03: injected by module.ts, not created here)
188
+ * @param projectId - GCP project that hosts the API Hub instance
189
+ * @param location - API Hub location (default: "global" — override if regional instance)
190
+ */
191
+ constructor(auth: GoogleAuth, projectId?: string, location?: string);
192
+ /** Base path prefix shared by all API Hub resource URLs for this instance. */
193
+ private resourcePrefix;
194
+ /**
195
+ * Fetches every page of a paginated API Hub list endpoint, following
196
+ * `nextPageToken` until the collection is exhausted, and returns the
197
+ * concatenated items. API Hub caps list responses (commonly at 50 entries per
198
+ * page), so callers MUST page or risk silently truncating the catalogue.
199
+ */
200
+ private fetchAllPages;
201
+ /**
202
+ * Lists all API definitions in this API Hub instance.
203
+ * Calls: GET /projects/{p}/locations/{l}/apis
204
+ *
205
+ * @param projectId - Optional override for the GCP project ID. When provided,
206
+ * overrides the instance-level projectId from the constructor. Allows a single
207
+ * ApiHubClient instance to serve multiple GCP projects (multi-org setups).
208
+ *
209
+ * Pages through all results, following nextPageToken until exhausted (API Hub
210
+ * caps list responses, commonly at 50 entries per page).
211
+ */
212
+ listApis(projectId?: string): Promise<ApiHubApi[]>;
213
+ /**
214
+ * Lists all versions for a given API.
215
+ * Calls: GET /projects/{p}/locations/{l}/apis/{apiId}/versions
216
+ *
217
+ * @param apiId - Short API ID (last segment of the full resource name)
218
+ * @param projectId - Optional GCP project override for multi-org setups; defaults to the instance projectId.
219
+ */
220
+ listVersions(apiId: string, projectId?: string): Promise<ApiHubVersion[]>;
221
+ /**
222
+ * Lists all specs attached to a given API version.
223
+ * Calls: GET /projects/{p}/locations/{l}/apis/{apiId}/versions/{versionId}/specs
224
+ *
225
+ * This is the authoritative source of spec IDs for a version. The versions
226
+ * list endpoint frequently returns version.specIds: null even when specs
227
+ * exist, so callers must enumerate specs here rather than trusting specIds.
228
+ *
229
+ * @param apiId - Short API ID (last segment of the full resource name)
230
+ * @param versionId - Short version ID (last segment of the version resource name)
231
+ * @param projectId - Optional GCP project override for multi-org setups; defaults to the instance projectId.
232
+ */
233
+ listSpecs(apiId: string, versionId: string, projectId?: string): Promise<ApiHubSpec[]>;
234
+ /**
235
+ * Fetches the spec metadata for a given API version and spec ID.
236
+ * Calls: GET /projects/{p}/locations/{l}/apis/{apiId}/versions/{versionId}/specs/{specId}
237
+ *
238
+ * To retrieve the actual spec file content (base64), call getSpecContents() instead.
239
+ *
240
+ * @param projectId - Optional GCP project override for multi-org setups; defaults to the instance projectId.
241
+ */
242
+ getSpec(apiId: string, versionId: string, specId: string, projectId?: string): Promise<ApiHubSpec>;
243
+ /**
244
+ * Fetches the raw spec file content for a given spec resource.
245
+ * Calls: GET .../specs/{specId}:contents
246
+ * Returns base64-encoded content in the `contents` field — caller must decode
247
+ * with Buffer.from(contents, 'base64').toString('utf-8').
248
+ *
249
+ * Used in Phase 3 for spec.definition population (with 500 KB size guard).
250
+ *
251
+ * @param projectId - Optional GCP project override for multi-org setups; defaults to the instance projectId.
252
+ */
253
+ getSpecContents(apiId: string, versionId: string, specId: string, projectId?: string): Promise<ApiHubSpecContents>;
254
+ }
255
+
256
+ /**
257
+ * Minimal representation of an Apigee sharedflow as returned by
258
+ * GET /v1/organizations/{org}/sharedflows
259
+ */
260
+ interface ApigeeSharedflow {
261
+ name: string;
262
+ latestRevisionId?: string;
263
+ metaData?: {
264
+ createdAt?: string;
265
+ lastModifiedAt?: string;
266
+ };
267
+ }
268
+ /**
269
+ * Client for the Apigee Management API's sharedflow endpoints.
270
+ */
271
+ declare class SharedflowClient {
272
+ private readonly auth;
273
+ constructor(auth: GoogleAuth);
274
+ /**
275
+ * Lists all sharedflows in the given Apigee organisation.
276
+ */
277
+ listSharedflows(org: string): Promise<ApigeeSharedflow[]>;
278
+ }
279
+
280
+ /**
281
+ * Per-organisation data gathered during a sync run.
282
+ * Populated by ApigeeEntityProvider.run() before calling proxyToEntities().
283
+ */
284
+ interface OrgSyncData {
285
+ /** Slugified org identifier (used as annotation key segment) */
286
+ slug: string;
287
+ /** Raw org name from config (e.g. "my-apigee-org") */
288
+ name: string;
289
+ /** GCP project ID owning this Apigee organisation */
290
+ projectId: string;
291
+ /** Names of environments this proxy is deployed to in this org */
292
+ environments: string[];
293
+ /** Resolved Backstage lifecycle for this org: production | experimental | deprecated */
294
+ lifecycle: string;
295
+ }
296
+ /**
297
+ * A proxy that has been deduplicated across organisations (D-03).
298
+ * Multiple orgs may share the same proxy name — they are merged into one entity.
299
+ */
300
+ interface MergedProxy {
301
+ /** Proxy data from the first org that declared this proxy */
302
+ proxy: ApigeeProxy;
303
+ /** Union of all base paths declared across all org revisions */
304
+ basePaths: string[];
305
+ /** All orgs that contain this proxy */
306
+ orgs: OrgSyncData[];
307
+ }
308
+ /** Options forwarded from plugin configuration. */
309
+ interface EntityBuildOptions {
310
+ /** Default spec.owner when not provided by API Hub. Must be a Backstage entity ref. */
311
+ defaultOwner?: string;
312
+ /** Inline spec size limit in bytes; default 512000 applied by the provider (D-03). */
313
+ specMaxBytes?: number;
314
+ }
315
+
316
+ /** Org configuration shape (matches config.d.ts apigee.orgs[n]) */
317
+ interface OrgConfig {
318
+ name: string;
319
+ projectId: string;
320
+ environments?: string[];
321
+ githubOrgSlug?: string;
322
+ /** proxyName → API Hub apiId override (D-01); set by module.ts from config. */
323
+ apiHubMappings?: Record<string, string>;
324
+ }
325
+ /**
326
+ * Entity provider that discovers Apigee API proxies and sharedflows across
327
+ * one or more organisations and registers them as Backstage entities.
328
+ *
329
+ * Registered with the catalog via `catalogProcessingExtensionPoint.addEntityProvider`.
330
+ * Scheduled via the `SchedulerServiceTaskRunner` injected at construction time.
331
+ */
332
+ declare class ApigeeEntityProvider implements EntityProvider {
333
+ private readonly taskRunner;
334
+ private readonly logger;
335
+ private readonly orgsConfig;
336
+ private readonly apigeeClient;
337
+ private readonly apiHubClient;
338
+ private readonly sharedflowClient;
339
+ private readonly options;
340
+ private connection?;
341
+ constructor(taskRunner: SchedulerServiceTaskRunner, logger: LoggerService, orgsConfig: OrgConfig[], apigeeClient: ApigeeClient, apiHubClient: ApiHubClient, sharedflowClient: SharedflowClient, options: EntityBuildOptions);
342
+ /** Stable provider name — MUST NOT change; it links entities to this provider. */
343
+ getProviderName(): string;
344
+ /**
345
+ * Called once by the catalog on startup. Stores the connection and registers
346
+ * the recurring sync task with the scheduler.
347
+ */
348
+ connect(connection: EntityProviderConnection): Promise<void>;
349
+ /**
350
+ * Executes one full catalog sync:
351
+ * 1. Fetch proxy / sharedflow / API-Hub data for all orgs in parallel.
352
+ * 2. Warn and skip any org that fails (error isolation per org).
353
+ * 3. Deduplicate proxies shared across multiple orgs (D-03).
354
+ * 4. Build entities and call applyMutation exactly once.
355
+ */
356
+ run(): Promise<void>;
357
+ /**
358
+ * Maps `items` through `fn` with at most `limit` invocations in flight at
359
+ * once, preserving input order in the returned array. Used to parallelise
360
+ * per-API spec resolution without overwhelming the API Hub backend (WR-03).
361
+ */
362
+ private mapWithConcurrency;
363
+ /**
364
+ * Fetches a spec's contents subject to the size guard (D-03).
365
+ *
366
+ * Two-stage guard:
367
+ * 1. Pre-fetch: if the spec metadata reports `sizeBytes > specMaxBytes`, skip
368
+ * the download entirely and return a `specUrl` link. NOTE: the API Hub spec
369
+ * metadata endpoint frequently reports `sizeBytes: 0`/undefined, so this
370
+ * stage often cannot fire and the contents are downloaded anyway.
371
+ * 2. Post-decode: enforce `specMaxBytes` against the actual decoded byte
372
+ * length — this is the guard that reliably bounds the *embedded* size.
373
+ *
374
+ * Because stage 1 is unreliable, `specMaxBytes` bounds the size of the spec
375
+ * that gets embedded, not necessarily the size downloaded into memory.
376
+ */
377
+ private fetchSpecEmbed;
378
+ /**
379
+ * Resolves the spec for an API: lists versions, deterministically selects one
380
+ * (D-02), enumerates that version's specs (the versions endpoint does not
381
+ * reliably populate specIds), picks the lexicographically smallest spec ID,
382
+ * then fetches it with the size guard. Per-spec failures are isolated: any
383
+ * error is logged and `undefined` is returned so one bad spec never aborts
384
+ * the whole sync.
385
+ */
386
+ private resolveSpecForApi;
387
+ /**
388
+ * Fetches all data for one org: proxies, their deployments + revisions,
389
+ * API Hub type map, and sharedflows.
390
+ */
391
+ private fetchOrgData;
392
+ }
393
+
394
+ /**
395
+ * A Backstage {@link CatalogProcessor} that links a backend `Component` to its
396
+ * proxy `API` entity by emitting the native `providesApi` / `apiProvidedBy`
397
+ * relation pair, surfacing the standard "Provided APIs" / "Providers" cards
398
+ * with zero custom UI (STITCH-01).
399
+ *
400
+ * Resolution dispatches through a strategy seam: a TargetServer-first path
401
+ * (deferred — STITCH-02, see 04-CONTEXT D-01) followed by the active
402
+ * direct-mapping path. The first strategy to return a target ref wins.
403
+ */
404
+ declare class ApigeeStitchingProcessor implements CatalogProcessor {
405
+ private readonly logger;
406
+ constructor(logger: LoggerService);
407
+ getProcessorName(): string;
408
+ postProcessEntity(entity: Entity, _location: LocationSpec, emit: CatalogProcessorEmit): Promise<Entity>;
409
+ /**
410
+ * Strategy seam. Tries the TargetServer-preferred path first (deferred this
411
+ * phase), then falls back to the active direct-mapping path. First strategy
412
+ * to return a ref wins.
413
+ */
414
+ private resolveTarget;
415
+ /**
416
+ * Deferred: TargetServer-preferred path (STITCH-02). See 04-CONTEXT D-01.
417
+ * No TargetServer data exists in the codebase yet, so this is the clean
418
+ * insertion point for the future branch and intentionally returns undefined.
419
+ */
420
+ private resolveViaTargetServer;
421
+ /**
422
+ * Direct-mapping path. Requires BOTH the api-name and project-id annotations.
423
+ * The proxy API entity ref is deterministic — `api:default/<slugify(api-name)>` —
424
+ * so no live catalog lookup is needed (D-01). projectId is validated for
425
+ * presence to confirm intent but is not part of the deterministic ref.
426
+ */
427
+ private resolveViaDirect;
428
+ }
429
+
430
+ export { ApiHubClient, ApigeeClient, ApigeeEntityProvider, ApigeeStitchingProcessor, SharedflowClient, catalogModuleApigee, apigeePlugin as default };
431
+ export type { ApiHubApi, ApiHubSpec, ApiHubSpecContents, ApiHubVersion, ApigeeDeployment, ApigeeProxy, ApigeeProxyRevision, ApigeeSharedflow, EntityBuildOptions, MergedProxy, OrgSyncData };