@cat-factory/contracts 0.334.0 → 0.335.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/dist/binary-generators.d.ts +4 -4
- package/dist/cache-policy.d.ts +22 -0
- package/dist/cache-policy.d.ts.map +1 -0
- package/dist/cache-policy.js +111 -0
- package/dist/cache-policy.js.map +1 -0
- package/dist/errors.d.ts +24 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +27 -1
- package/dist/errors.js.map +1 -1
- package/dist/foundational-services.d.ts +16 -9
- package/dist/foundational-services.d.ts.map +1 -1
- package/dist/foundational-services.js +20 -1
- package/dist/foundational-services.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/observability.d.ts +57 -0
- package/dist/observability.d.ts.map +1 -1
- package/dist/observability.js +19 -0
- package/dist/observability.js.map +1 -1
- package/dist/openrouter.d.ts +111 -4
- package/dist/openrouter.d.ts.map +1 -1
- package/dist/openrouter.js +39 -1
- package/dist/openrouter.js.map +1 -1
- package/dist/reserved-env-keys.d.ts.map +1 -1
- package/dist/reserved-env-keys.js +6 -0
- package/dist/reserved-env-keys.js.map +1 -1
- package/dist/routes/execution.d.ts +4 -0
- package/dist/routes/execution.d.ts.map +1 -1
- package/dist/routes/foundational-services.d.ts +8 -8
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/openrouter.d.ts +16 -0
- package/dist/routes/openrouter.d.ts.map +1 -1
- package/dist/routes/service-catalog.d.ts +298 -0
- package/dist/routes/service-catalog.d.ts.map +1 -0
- package/dist/routes/service-catalog.js +77 -0
- package/dist/routes/service-catalog.js.map +1 -0
- package/dist/service-catalog.d.ts +223 -0
- package/dist/service-catalog.d.ts.map +1 -0
- package/dist/service-catalog.js +234 -0
- package/dist/service-catalog.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
/**
|
|
3
|
+
* Which developer-portal product a connection talks to.
|
|
4
|
+
*
|
|
5
|
+
* A picklist with one member rather than a bare literal, because the read surface, the SPA and
|
|
6
|
+
* the stored row all name it: adding Port / Cortex / OpsLevel later is a member plus an adapter,
|
|
7
|
+
* and every `Record<ServiceCatalogProvider, …>` fails to compile until it is mapped. The
|
|
8
|
+
* platform-neutral vocabulary below (`ServiceCatalogEntry` in the kernel) is what the importer
|
|
9
|
+
* consumes, so nothing downstream of the adapter knows the word "Backstage".
|
|
10
|
+
*/
|
|
11
|
+
export declare const serviceCatalogProviderSchema: v.PicklistSchema<["backstage"], undefined>;
|
|
12
|
+
export type ServiceCatalogProvider = v.InferOutput<typeof serviceCatalogProviderSchema>;
|
|
13
|
+
/**
|
|
14
|
+
* How the platform authenticates to a SELF-HOSTED portal.
|
|
15
|
+
*
|
|
16
|
+
* These are the shapes organisations actually run Backstage behind, and each needs a different
|
|
17
|
+
* request built, which is why this is a closed vocabulary rather than a free-form header bag:
|
|
18
|
+
*
|
|
19
|
+
* - `none`: no auth on the catalog API. Real, and common on an instance reachable only inside a
|
|
20
|
+
* VPN. It is admissible ONLY because the base URL is held to the deployment's
|
|
21
|
+
* `UrlSafetyPolicy`, so reaching a private host is an operator decision already made.
|
|
22
|
+
* - `static-token`: a service-to-service token from Backstage's `backend.auth.externalAccess`
|
|
23
|
+
* (`type: static`), sent as `Authorization: Bearer <token>`. The vendor's own recommended
|
|
24
|
+
* way for an external system to read the catalog.
|
|
25
|
+
* - `legacy-shared-secret`: the older external-access route, still what a great many
|
|
26
|
+
* self-hosted instances are configured for: a short-lived HS256 JWT signed with a secret
|
|
27
|
+
* from `backend.auth.keys[]`. The platform MINTS the token per request rather than storing
|
|
28
|
+
* one, which is why the stored credential is the secret and not a bearer value.
|
|
29
|
+
* - `oauth2-client-credentials`: the portal sits behind an IdP or an identity-aware proxy
|
|
30
|
+
* (Keycloak, Okta, Entra, Google IAP). The platform exchanges a client id/secret at the
|
|
31
|
+
* token endpoint and sends the result as a bearer token.
|
|
32
|
+
* - `basic`: HTTP Basic, i.e. a reverse proxy in front of the portal.
|
|
33
|
+
* - `headers`: an explicit header list, for a gateway that authenticates on its own header
|
|
34
|
+
* names. A LIST rather than one pair because the common case needs two (a Cloudflare Access
|
|
35
|
+
* service token is `CF-Access-Client-Id` plus `CF-Access-Client-Secret`), and a single-pair
|
|
36
|
+
* shape would have sent half a credential.
|
|
37
|
+
*/
|
|
38
|
+
export declare const serviceCatalogAuthModeSchema: v.PicklistSchema<["none", "static-token", "legacy-shared-secret", "oauth2-client-credentials", "basic", "headers"], undefined>;
|
|
39
|
+
export type ServiceCatalogAuthMode = v.InferOutput<typeof serviceCatalogAuthModeSchema>;
|
|
40
|
+
/** The credential half of a connection, discriminated by the auth mode it belongs to. */
|
|
41
|
+
export declare const serviceCatalogAuthSchema: v.VariantSchema<"mode", [v.ObjectSchema<{
|
|
42
|
+
readonly mode: v.LiteralSchema<"none", undefined>;
|
|
43
|
+
}, undefined>, v.ObjectSchema<{
|
|
44
|
+
readonly mode: v.LiteralSchema<"static-token", undefined>;
|
|
45
|
+
readonly token: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>]>;
|
|
46
|
+
}, undefined>, v.ObjectSchema<{
|
|
47
|
+
readonly mode: v.LiteralSchema<"legacy-shared-secret", undefined>;
|
|
48
|
+
readonly sharedSecret: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>]>;
|
|
49
|
+
}, undefined>, v.ObjectSchema<{
|
|
50
|
+
readonly mode: v.LiteralSchema<"oauth2-client-credentials", undefined>;
|
|
51
|
+
/** The IdP's token endpoint. Held to the same URL policy as the portal's own base URL. */
|
|
52
|
+
readonly tokenUrl: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 500, undefined>]>;
|
|
53
|
+
readonly clientId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 200, undefined>]>;
|
|
54
|
+
readonly clientSecret: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>]>;
|
|
55
|
+
/** Optional `scope` / `audience` request parameters some IdPs require. */
|
|
56
|
+
readonly scope: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MaxLengthAction<string, 500, undefined>]>, undefined>;
|
|
57
|
+
readonly audience: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MaxLengthAction<string, 500, undefined>]>, undefined>;
|
|
58
|
+
}, undefined>, v.ObjectSchema<{
|
|
59
|
+
readonly mode: v.LiteralSchema<"basic", undefined>;
|
|
60
|
+
readonly username: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 200, undefined>]>;
|
|
61
|
+
readonly password: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>]>;
|
|
62
|
+
}, undefined>, v.ObjectSchema<{
|
|
63
|
+
readonly mode: v.LiteralSchema<"headers", undefined>;
|
|
64
|
+
readonly headers: v.SchemaWithPipe<readonly [v.ArraySchema<v.ObjectSchema<{
|
|
65
|
+
readonly name: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 100, undefined>, v.RegexAction<string, "must be a valid HTTP header name">]>;
|
|
66
|
+
readonly value: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>, v.RegexAction<string, "must not contain a line break">]>;
|
|
67
|
+
}, undefined>, undefined>, v.MinLengthAction<{
|
|
68
|
+
name: string;
|
|
69
|
+
value: string;
|
|
70
|
+
}[], 1, undefined>, v.MaxLengthAction<{
|
|
71
|
+
name: string;
|
|
72
|
+
value: string;
|
|
73
|
+
}[], 5, undefined>]>;
|
|
74
|
+
}, undefined>], undefined>;
|
|
75
|
+
export type ServiceCatalogAuth = v.InferOutput<typeof serviceCatalogAuthSchema>;
|
|
76
|
+
/**
|
|
77
|
+
* The default filter, applied when a connection names none: the portal's SERVICE-like
|
|
78
|
+
* components.
|
|
79
|
+
*
|
|
80
|
+
* A default rather than a required field, because the honest alternative is worse in both
|
|
81
|
+
* directions. Importing the WHOLE catalog would fold a thousand-entity estate into every
|
|
82
|
+
* design prompt (the exact cost ADR 0031 split the catalog and contract reads to avoid), while
|
|
83
|
+
* refusing to import without a filter makes the first connection fail on a vendor query language
|
|
84
|
+
* the operator has no reason to know yet. `kind=component` is the one term that is right for
|
|
85
|
+
* every Backstage instance, and a narrower estate (a tag, a system, an owner) is one term more.
|
|
86
|
+
*/
|
|
87
|
+
export declare const DEFAULT_SERVICE_CATALOG_FILTER: readonly string[];
|
|
88
|
+
/** How many services ONE import may bring in, and the ceiling on that number. */
|
|
89
|
+
export declare const DEFAULT_MAX_CATALOG_SERVICES = 200;
|
|
90
|
+
export declare const MAX_CATALOG_SERVICES_CEILING = 1000;
|
|
91
|
+
/** Connect (or re-connect) the workspace's service catalog. Replaces any prior connection. */
|
|
92
|
+
export declare const connectServiceCatalogSchema: v.ObjectSchema<{
|
|
93
|
+
readonly provider: v.OptionalSchema<v.PicklistSchema<["backstage"], undefined>, undefined>;
|
|
94
|
+
/** The portal's base URL, e.g. `https://backstage.corp.internal`. */
|
|
95
|
+
readonly baseUrl: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 500, undefined>]>;
|
|
96
|
+
readonly auth: v.VariantSchema<"mode", [v.ObjectSchema<{
|
|
97
|
+
readonly mode: v.LiteralSchema<"none", undefined>;
|
|
98
|
+
}, undefined>, v.ObjectSchema<{
|
|
99
|
+
readonly mode: v.LiteralSchema<"static-token", undefined>;
|
|
100
|
+
readonly token: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>]>;
|
|
101
|
+
}, undefined>, v.ObjectSchema<{
|
|
102
|
+
readonly mode: v.LiteralSchema<"legacy-shared-secret", undefined>;
|
|
103
|
+
readonly sharedSecret: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>]>;
|
|
104
|
+
}, undefined>, v.ObjectSchema<{
|
|
105
|
+
readonly mode: v.LiteralSchema<"oauth2-client-credentials", undefined>;
|
|
106
|
+
/** The IdP's token endpoint. Held to the same URL policy as the portal's own base URL. */
|
|
107
|
+
readonly tokenUrl: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 500, undefined>]>;
|
|
108
|
+
readonly clientId: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 200, undefined>]>;
|
|
109
|
+
readonly clientSecret: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>]>;
|
|
110
|
+
/** Optional `scope` / `audience` request parameters some IdPs require. */
|
|
111
|
+
readonly scope: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MaxLengthAction<string, 500, undefined>]>, undefined>;
|
|
112
|
+
readonly audience: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MaxLengthAction<string, 500, undefined>]>, undefined>;
|
|
113
|
+
}, undefined>, v.ObjectSchema<{
|
|
114
|
+
readonly mode: v.LiteralSchema<"basic", undefined>;
|
|
115
|
+
readonly username: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 200, undefined>]>;
|
|
116
|
+
readonly password: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>]>;
|
|
117
|
+
}, undefined>, v.ObjectSchema<{
|
|
118
|
+
readonly mode: v.LiteralSchema<"headers", undefined>;
|
|
119
|
+
readonly headers: v.SchemaWithPipe<readonly [v.ArraySchema<v.ObjectSchema<{
|
|
120
|
+
readonly name: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 100, undefined>, v.RegexAction<string, "must be a valid HTTP header name">]>;
|
|
121
|
+
readonly value: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 8000, undefined>, v.RegexAction<string, "must not contain a line break">]>;
|
|
122
|
+
}, undefined>, undefined>, v.MinLengthAction<{
|
|
123
|
+
name: string;
|
|
124
|
+
value: string;
|
|
125
|
+
}[], 1, undefined>, v.MaxLengthAction<{
|
|
126
|
+
name: string;
|
|
127
|
+
value: string;
|
|
128
|
+
}[], 5, undefined>]>;
|
|
129
|
+
}, undefined>], undefined>;
|
|
130
|
+
/**
|
|
131
|
+
* Portal-side filter terms, ANDed. Empty/absent ⇒ {@link DEFAULT_SERVICE_CATALOG_FILTER}.
|
|
132
|
+
* Filtering AT THE PORTAL rather than after the fetch is what keeps a large estate from being
|
|
133
|
+
* paged through in full on every refresh.
|
|
134
|
+
*/
|
|
135
|
+
readonly entityFilter: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.TrimAction, v.MinLengthAction<string, 3, undefined>, v.MaxLengthAction<string, 200, undefined>, v.RegexAction<string, "must be a single `key=value` term (letters, digits and `_.-:/@`)">]>, undefined>, v.MaxLengthAction<string[], 10, undefined>]>, undefined>;
|
|
136
|
+
/**
|
|
137
|
+
* Whether to import the API definitions each service publishes (`spec.providesApis`).
|
|
138
|
+
* Defaults to true, being the half of the ask that lets a consumer call a shared service
|
|
139
|
+
* rather than merely know it exists.
|
|
140
|
+
*/
|
|
141
|
+
readonly includeApis: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
142
|
+
/** Cap on imported services. Defaults to {@link DEFAULT_MAX_CATALOG_SERVICES}. */
|
|
143
|
+
readonly maxServices: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>, v.MaxValueAction<number, 1000, undefined>]>, undefined>;
|
|
144
|
+
}, undefined>;
|
|
145
|
+
export type ConnectServiceCatalogInput = v.InferOutput<typeof connectServiceCatalogSchema>;
|
|
146
|
+
/**
|
|
147
|
+
* How much of the portal's matching estate one import covered.
|
|
148
|
+
*
|
|
149
|
+
* Three states rather than a count, for the reason the repo-source `folderScan` coverage has
|
|
150
|
+
* three: each needs a different fix from the person who connected it, and `truncated` must never
|
|
151
|
+
* read as `complete`. An importer that reported only "42 services" would leave an operator with
|
|
152
|
+
* a 300-service estate believing the catalog is the estate.
|
|
153
|
+
*/
|
|
154
|
+
export declare const serviceCatalogCoverageSchema: v.PicklistSchema<["complete", "truncated", "empty"], undefined>;
|
|
155
|
+
export type ServiceCatalogCoverage = v.InferOutput<typeof serviceCatalogCoverageSchema>;
|
|
156
|
+
/** What the LAST import concluded, as the management surface reports it. */
|
|
157
|
+
export declare const serviceCatalogSyncStatusSchema: v.PicklistSchema<["ok", "partial", "failed"], undefined>;
|
|
158
|
+
export type ServiceCatalogSyncStatus = v.InferOutput<typeof serviceCatalogSyncStatusSchema>;
|
|
159
|
+
/**
|
|
160
|
+
* The connection as the management surface sees it: never a credential, not even a masked one.
|
|
161
|
+
*
|
|
162
|
+
* `authMode` is here because it is the one part of the credential that is configuration rather
|
|
163
|
+
* than secret, because the operator has to see WHICH scheme a stored connection uses to know whether
|
|
164
|
+
* it still matches how the portal is deployed.
|
|
165
|
+
*/
|
|
166
|
+
export declare const serviceCatalogConnectionSchema: v.ObjectSchema<{
|
|
167
|
+
readonly provider: v.PicklistSchema<["backstage"], undefined>;
|
|
168
|
+
readonly baseUrl: v.StringSchema<undefined>;
|
|
169
|
+
readonly authMode: v.PicklistSchema<["none", "static-token", "legacy-shared-secret", "oauth2-client-credentials", "basic", "headers"], undefined>;
|
|
170
|
+
readonly entityFilter: v.ArraySchema<v.StringSchema<undefined>, undefined>;
|
|
171
|
+
readonly includeApis: v.BooleanSchema<undefined>;
|
|
172
|
+
readonly maxServices: v.NumberSchema<undefined>;
|
|
173
|
+
readonly lastSyncedAt: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
174
|
+
readonly lastSyncStatus: v.NullableSchema<v.PicklistSchema<["ok", "partial", "failed"], undefined>, undefined>;
|
|
175
|
+
/**
|
|
176
|
+
* What the last import wants a human to know: the truncation, the unreadable definitions, or
|
|
177
|
+
* the transport failure. Null when the last pass had nothing to report, which is the only
|
|
178
|
+
* state that needs no sentence.
|
|
179
|
+
*/
|
|
180
|
+
readonly lastSyncMessage: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
181
|
+
readonly connectedAt: v.NumberSchema<undefined>;
|
|
182
|
+
}, undefined>;
|
|
183
|
+
export type ServiceCatalogConnection = v.InferOutput<typeof serviceCatalogConnectionSchema>;
|
|
184
|
+
/** Outcome of one import: what changed in the catalog, and what the pass could not do. */
|
|
185
|
+
export declare const serviceCatalogSyncResultSchema: v.ObjectSchema<{
|
|
186
|
+
readonly upserted: v.NumberSchema<undefined>;
|
|
187
|
+
/** Services this connection produced before and the portal no longer offers. */
|
|
188
|
+
readonly tombstoned: v.NumberSchema<undefined>;
|
|
189
|
+
readonly unchanged: v.NumberSchema<undefined>;
|
|
190
|
+
/** API definitions stored across every imported service. */
|
|
191
|
+
readonly contracts: v.NumberSchema<undefined>;
|
|
192
|
+
readonly coverage: v.PicklistSchema<["complete", "truncated", "empty"], undefined>;
|
|
193
|
+
/**
|
|
194
|
+
* Entities the filter matched that did NOT become a service: no usable identity, or a name
|
|
195
|
+
* that yields no slug. Zero and "nothing matched" are told apart by {@link coverage}, and a
|
|
196
|
+
* non-zero value here is the only thing that explains a catalog thinner than the portal's
|
|
197
|
+
* own entity count.
|
|
198
|
+
*/
|
|
199
|
+
readonly skippedServices: v.NumberSchema<undefined>;
|
|
200
|
+
/**
|
|
201
|
+
* Portal services this pass REFUSED to write because the workspace already registers a service
|
|
202
|
+
* under that id by another route (an upload, a linked repo).
|
|
203
|
+
*
|
|
204
|
+
* Its own count rather than folded into {@link skippedServices}, because the two name opposite
|
|
205
|
+
* faults with opposite remedies: a skipped service is an entity the PORTAL describes in a way
|
|
206
|
+
* this platform cannot use, and this is a collision between two things the workspace itself
|
|
207
|
+
* did. Refusing is the only safe disposition, since overwriting would silently replace a
|
|
208
|
+
* hand-authored service (and its uploaded contracts, and any platform capability it was granted)
|
|
209
|
+
* with the portal's row, and disconnecting would then tombstone the original.
|
|
210
|
+
*/
|
|
211
|
+
readonly skippedConflicts: v.NumberSchema<undefined>;
|
|
212
|
+
/**
|
|
213
|
+
* API definitions a service DECLARES that this pass could not store: a type the platform
|
|
214
|
+
* serves no format for, an entity the portal would not return, an empty or oversized
|
|
215
|
+
* definition, or an id that collides with another interface of the same service. Reported
|
|
216
|
+
* rather than dropped, because a service listed with no interface reads to an agent as a
|
|
217
|
+
* service that publishes none.
|
|
218
|
+
*/
|
|
219
|
+
readonly skippedApis: v.NumberSchema<undefined>;
|
|
220
|
+
readonly status: v.PicklistSchema<["ok", "partial", "failed"], undefined>;
|
|
221
|
+
}, undefined>;
|
|
222
|
+
export type ServiceCatalogSyncResult = v.InferOutput<typeof serviceCatalogSyncResultSchema>;
|
|
223
|
+
//# sourceMappingURL=service-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-catalog.d.ts","sourceRoot":"","sources":["../src/service-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAqB5B;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,4CAA4B,CAAA;AACrE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,4BAA4B,CAAC,CAAA;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,4BAA4B,gIAOvC,CAAA;AACF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,4BAA4B,CAAC,CAAA;AA0BvF,yFAAyF;AACzF,eAAO,MAAM,wBAAwB;;;;;;;;;;IAMjC,0FAA0F;;;;IAI1F,0EAA0E;;;;;;;;;;;;;;;;;;;0BAa5E,CAAA;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAqB/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,EAAE,SAAS,MAAM,EAAuB,CAAA;AAEnF,iFAAiF;AACjF,eAAO,MAAM,4BAA4B,MAAM,CAAA;AAC/C,eAAO,MAAM,4BAA4B,OAAQ,CAAA;AAEjD,8FAA8F;AAC9F,eAAO,MAAM,2BAA2B;;IAEtC,qEAAqE;;;;;;;;;;;;QA3DnE,0FAA0F;;;;QAI1F,0EAA0E;;;;;;;;;;;;;;;;;;;;IA0D5E;;;;OAIG;;IAEH;;;;OAIG;;IAEH,kFAAkF;;aAIlF,CAAA;AACF,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAE1F;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,iEAWvC,CAAA;AACF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,4BAA4B,CAAC,CAAA;AAEvF,4EAA4E;AAC5E,eAAO,MAAM,8BAA8B,0DAWzC,CAAA;AACF,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,8BAA8B,CAAC,CAAA;AAE3F;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;IASzC;;;;OAIG;;;aAGH,CAAA;AACF,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,8BAA8B,CAAC,CAAA;AAE3F,0FAA0F;AAC1F,eAAO,MAAM,8BAA8B;;IAEzC,gFAAgF;;;IAGhF,4DAA4D;;;IAG5D;;;;;OAKG;;IAEH;;;;;;;;;;OAUG;;IAEH;;;;;;OAMG;;;aAGH,CAAA;AACF,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,8BAA8B,CAAC,CAAA"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Wire contracts for the SERVICE CATALOG connection: a workspace points the platform at the
|
|
4
|
+
// developer portal its organisation already runs (Backstage today) and the platform imports
|
|
5
|
+
// that portal's services (identity, ownership, and the API definitions each one publishes)
|
|
6
|
+
// into the FOUNDATIONAL SERVICES catalog agents already read
|
|
7
|
+
// (backend/docs/adr/0031-foundational-services.md).
|
|
8
|
+
//
|
|
9
|
+
// It is a THIRD supply route beside direct upload and a linked git repo, deliberately not a
|
|
10
|
+
// second agent-facing catalog: an agent that was told about a service twice, once per
|
|
11
|
+
// mechanism, is the accretion this reuse avoids. What the import produces is ordinary
|
|
12
|
+
// `workspace`-tier foundational services, so the tiered merge, the suppression sub-resource,
|
|
13
|
+
// the lazily-read contract documents and the injected `.cat-context/` files all work unchanged.
|
|
14
|
+
//
|
|
15
|
+
// Scoped to a WORKSPACE, like every other sealed vendor connection (observability, tracker,
|
|
16
|
+
// document sources). That is also what makes it work on a mothership-mode node: the credential
|
|
17
|
+
// bag travels as ciphertext and is opened by naming the row over the secret delegation, which
|
|
18
|
+
// is workspace-keyed by construction.
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Which developer-portal product a connection talks to.
|
|
22
|
+
*
|
|
23
|
+
* A picklist with one member rather than a bare literal, because the read surface, the SPA and
|
|
24
|
+
* the stored row all name it: adding Port / Cortex / OpsLevel later is a member plus an adapter,
|
|
25
|
+
* and every `Record<ServiceCatalogProvider, …>` fails to compile until it is mapped. The
|
|
26
|
+
* platform-neutral vocabulary below (`ServiceCatalogEntry` in the kernel) is what the importer
|
|
27
|
+
* consumes, so nothing downstream of the adapter knows the word "Backstage".
|
|
28
|
+
*/
|
|
29
|
+
export const serviceCatalogProviderSchema = v.picklist(['backstage']);
|
|
30
|
+
/**
|
|
31
|
+
* How the platform authenticates to a SELF-HOSTED portal.
|
|
32
|
+
*
|
|
33
|
+
* These are the shapes organisations actually run Backstage behind, and each needs a different
|
|
34
|
+
* request built, which is why this is a closed vocabulary rather than a free-form header bag:
|
|
35
|
+
*
|
|
36
|
+
* - `none`: no auth on the catalog API. Real, and common on an instance reachable only inside a
|
|
37
|
+
* VPN. It is admissible ONLY because the base URL is held to the deployment's
|
|
38
|
+
* `UrlSafetyPolicy`, so reaching a private host is an operator decision already made.
|
|
39
|
+
* - `static-token`: a service-to-service token from Backstage's `backend.auth.externalAccess`
|
|
40
|
+
* (`type: static`), sent as `Authorization: Bearer <token>`. The vendor's own recommended
|
|
41
|
+
* way for an external system to read the catalog.
|
|
42
|
+
* - `legacy-shared-secret`: the older external-access route, still what a great many
|
|
43
|
+
* self-hosted instances are configured for: a short-lived HS256 JWT signed with a secret
|
|
44
|
+
* from `backend.auth.keys[]`. The platform MINTS the token per request rather than storing
|
|
45
|
+
* one, which is why the stored credential is the secret and not a bearer value.
|
|
46
|
+
* - `oauth2-client-credentials`: the portal sits behind an IdP or an identity-aware proxy
|
|
47
|
+
* (Keycloak, Okta, Entra, Google IAP). The platform exchanges a client id/secret at the
|
|
48
|
+
* token endpoint and sends the result as a bearer token.
|
|
49
|
+
* - `basic`: HTTP Basic, i.e. a reverse proxy in front of the portal.
|
|
50
|
+
* - `headers`: an explicit header list, for a gateway that authenticates on its own header
|
|
51
|
+
* names. A LIST rather than one pair because the common case needs two (a Cloudflare Access
|
|
52
|
+
* service token is `CF-Access-Client-Id` plus `CF-Access-Client-Secret`), and a single-pair
|
|
53
|
+
* shape would have sent half a credential.
|
|
54
|
+
*/
|
|
55
|
+
export const serviceCatalogAuthModeSchema = v.picklist([
|
|
56
|
+
'none',
|
|
57
|
+
'static-token',
|
|
58
|
+
'legacy-shared-secret',
|
|
59
|
+
'oauth2-client-credentials',
|
|
60
|
+
'basic',
|
|
61
|
+
'headers',
|
|
62
|
+
]);
|
|
63
|
+
const secretValue = v.pipe(v.string(), v.trim(), v.minLength(1), v.maxLength(8_000));
|
|
64
|
+
const shortText = v.pipe(v.string(), v.trim(), v.minLength(1), v.maxLength(200));
|
|
65
|
+
const url = v.pipe(v.string(), v.trim(), v.minLength(1), v.maxLength(500));
|
|
66
|
+
/**
|
|
67
|
+
* A header name a gateway may authenticate on. Restricted to the RFC 7230 token characters this
|
|
68
|
+
* platform will emit, so a stored credential can never inject a second header or a request line
|
|
69
|
+
* (the value is checked the same way below, because a `\r\n` in either half is the same split).
|
|
70
|
+
*/
|
|
71
|
+
const headerName = v.pipe(v.string(), v.trim(), v.minLength(1), v.maxLength(100), v.regex(/^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/, 'must be a valid HTTP header name'));
|
|
72
|
+
const headerValue = v.pipe(v.string(), v.minLength(1), v.maxLength(8_000), v.regex(/^[^\r\n]+$/, 'must not contain a line break'));
|
|
73
|
+
/** The credential half of a connection, discriminated by the auth mode it belongs to. */
|
|
74
|
+
export const serviceCatalogAuthSchema = v.variant('mode', [
|
|
75
|
+
v.object({ mode: v.literal('none') }),
|
|
76
|
+
v.object({ mode: v.literal('static-token'), token: secretValue }),
|
|
77
|
+
v.object({ mode: v.literal('legacy-shared-secret'), sharedSecret: secretValue }),
|
|
78
|
+
v.object({
|
|
79
|
+
mode: v.literal('oauth2-client-credentials'),
|
|
80
|
+
/** The IdP's token endpoint. Held to the same URL policy as the portal's own base URL. */
|
|
81
|
+
tokenUrl: url,
|
|
82
|
+
clientId: shortText,
|
|
83
|
+
clientSecret: secretValue,
|
|
84
|
+
/** Optional `scope` / `audience` request parameters some IdPs require. */
|
|
85
|
+
scope: v.optional(v.pipe(v.string(), v.trim(), v.maxLength(500))),
|
|
86
|
+
audience: v.optional(v.pipe(v.string(), v.trim(), v.maxLength(500))),
|
|
87
|
+
}),
|
|
88
|
+
v.object({ mode: v.literal('basic'), username: shortText, password: secretValue }),
|
|
89
|
+
v.object({
|
|
90
|
+
mode: v.literal('headers'),
|
|
91
|
+
headers: v.pipe(v.array(v.object({ name: headerName, value: headerValue })), v.minLength(1), v.maxLength(5)),
|
|
92
|
+
}),
|
|
93
|
+
]);
|
|
94
|
+
/**
|
|
95
|
+
* One term of the portal-side entity filter, `key=value`.
|
|
96
|
+
*
|
|
97
|
+
* The charset is deliberately tight: these terms are serialised into the vendor's query string,
|
|
98
|
+
* so admitting `&` or `=` beyond the first would let a stored filter add query parameters the
|
|
99
|
+
* platform never meant to send (a `limit`, a second `filter`, a `fields` projection that drops
|
|
100
|
+
* the fields the importer reads). One `=`, no separators, no whitespace.
|
|
101
|
+
*/
|
|
102
|
+
const entityFilterTerm = v.pipe(v.string(), v.trim(), v.minLength(3), v.maxLength(200), v.regex(/^[A-Za-z0-9_.-]+=[A-Za-z0-9_.:/@-]+$/, 'must be a single `key=value` term (letters, digits and `_.-:/@`)'));
|
|
103
|
+
/**
|
|
104
|
+
* The default filter, applied when a connection names none: the portal's SERVICE-like
|
|
105
|
+
* components.
|
|
106
|
+
*
|
|
107
|
+
* A default rather than a required field, because the honest alternative is worse in both
|
|
108
|
+
* directions. Importing the WHOLE catalog would fold a thousand-entity estate into every
|
|
109
|
+
* design prompt (the exact cost ADR 0031 split the catalog and contract reads to avoid), while
|
|
110
|
+
* refusing to import without a filter makes the first connection fail on a vendor query language
|
|
111
|
+
* the operator has no reason to know yet. `kind=component` is the one term that is right for
|
|
112
|
+
* every Backstage instance, and a narrower estate (a tag, a system, an owner) is one term more.
|
|
113
|
+
*/
|
|
114
|
+
export const DEFAULT_SERVICE_CATALOG_FILTER = ['kind=component'];
|
|
115
|
+
/** How many services ONE import may bring in, and the ceiling on that number. */
|
|
116
|
+
export const DEFAULT_MAX_CATALOG_SERVICES = 200;
|
|
117
|
+
export const MAX_CATALOG_SERVICES_CEILING = 1_000;
|
|
118
|
+
/** Connect (or re-connect) the workspace's service catalog. Replaces any prior connection. */
|
|
119
|
+
export const connectServiceCatalogSchema = v.object({
|
|
120
|
+
provider: v.optional(serviceCatalogProviderSchema),
|
|
121
|
+
/** The portal's base URL, e.g. `https://backstage.corp.internal`. */
|
|
122
|
+
baseUrl: url,
|
|
123
|
+
auth: serviceCatalogAuthSchema,
|
|
124
|
+
/**
|
|
125
|
+
* Portal-side filter terms, ANDed. Empty/absent ⇒ {@link DEFAULT_SERVICE_CATALOG_FILTER}.
|
|
126
|
+
* Filtering AT THE PORTAL rather than after the fetch is what keeps a large estate from being
|
|
127
|
+
* paged through in full on every refresh.
|
|
128
|
+
*/
|
|
129
|
+
entityFilter: v.optional(v.pipe(v.array(entityFilterTerm), v.maxLength(10))),
|
|
130
|
+
/**
|
|
131
|
+
* Whether to import the API definitions each service publishes (`spec.providesApis`).
|
|
132
|
+
* Defaults to true, being the half of the ask that lets a consumer call a shared service
|
|
133
|
+
* rather than merely know it exists.
|
|
134
|
+
*/
|
|
135
|
+
includeApis: v.optional(v.boolean()),
|
|
136
|
+
/** Cap on imported services. Defaults to {@link DEFAULT_MAX_CATALOG_SERVICES}. */
|
|
137
|
+
maxServices: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(MAX_CATALOG_SERVICES_CEILING))),
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* How much of the portal's matching estate one import covered.
|
|
141
|
+
*
|
|
142
|
+
* Three states rather than a count, for the reason the repo-source `folderScan` coverage has
|
|
143
|
+
* three: each needs a different fix from the person who connected it, and `truncated` must never
|
|
144
|
+
* read as `complete`. An importer that reported only "42 services" would leave an operator with
|
|
145
|
+
* a 300-service estate believing the catalog is the estate.
|
|
146
|
+
*/
|
|
147
|
+
export const serviceCatalogCoverageSchema = v.picklist([
|
|
148
|
+
/** Every matching entity was imported. */
|
|
149
|
+
'complete',
|
|
150
|
+
/** The `maxServices` cap stopped the paging short, so the catalog is a PREFIX of the estate. */
|
|
151
|
+
'truncated',
|
|
152
|
+
/**
|
|
153
|
+
* The filter matched nothing at all. Kept apart from `complete` because zero services is
|
|
154
|
+
* never a healthy `complete`: it is a filter that names a kind, tag or system the portal does
|
|
155
|
+
* not use, and the remedy is editing the filter rather than waiting for a sync.
|
|
156
|
+
*/
|
|
157
|
+
'empty',
|
|
158
|
+
]);
|
|
159
|
+
/** What the LAST import concluded, as the management surface reports it. */
|
|
160
|
+
export const serviceCatalogSyncStatusSchema = v.picklist([
|
|
161
|
+
/** Everything the filter matched arrived, with nothing dropped. */
|
|
162
|
+
'ok',
|
|
163
|
+
/**
|
|
164
|
+
* The import ran and is INCOMPLETE: truncated by the cap, or with entities/definitions it
|
|
165
|
+
* could not use. Distinct from `failed`: the catalog holds real services, it just does not
|
|
166
|
+
* hold all of them, and only naming that keeps a partial estate from reading as the estate.
|
|
167
|
+
*/
|
|
168
|
+
'partial',
|
|
169
|
+
/** The import could not read the portal at all; whatever the catalog holds is from before. */
|
|
170
|
+
'failed',
|
|
171
|
+
]);
|
|
172
|
+
/**
|
|
173
|
+
* The connection as the management surface sees it: never a credential, not even a masked one.
|
|
174
|
+
*
|
|
175
|
+
* `authMode` is here because it is the one part of the credential that is configuration rather
|
|
176
|
+
* than secret, because the operator has to see WHICH scheme a stored connection uses to know whether
|
|
177
|
+
* it still matches how the portal is deployed.
|
|
178
|
+
*/
|
|
179
|
+
export const serviceCatalogConnectionSchema = v.object({
|
|
180
|
+
provider: serviceCatalogProviderSchema,
|
|
181
|
+
baseUrl: v.string(),
|
|
182
|
+
authMode: serviceCatalogAuthModeSchema,
|
|
183
|
+
entityFilter: v.array(v.string()),
|
|
184
|
+
includeApis: v.boolean(),
|
|
185
|
+
maxServices: v.number(),
|
|
186
|
+
lastSyncedAt: v.nullable(v.number()),
|
|
187
|
+
lastSyncStatus: v.nullable(serviceCatalogSyncStatusSchema),
|
|
188
|
+
/**
|
|
189
|
+
* What the last import wants a human to know: the truncation, the unreadable definitions, or
|
|
190
|
+
* the transport failure. Null when the last pass had nothing to report, which is the only
|
|
191
|
+
* state that needs no sentence.
|
|
192
|
+
*/
|
|
193
|
+
lastSyncMessage: v.nullable(v.string()),
|
|
194
|
+
connectedAt: v.number(),
|
|
195
|
+
});
|
|
196
|
+
/** Outcome of one import: what changed in the catalog, and what the pass could not do. */
|
|
197
|
+
export const serviceCatalogSyncResultSchema = v.object({
|
|
198
|
+
upserted: v.number(),
|
|
199
|
+
/** Services this connection produced before and the portal no longer offers. */
|
|
200
|
+
tombstoned: v.number(),
|
|
201
|
+
unchanged: v.number(),
|
|
202
|
+
/** API definitions stored across every imported service. */
|
|
203
|
+
contracts: v.number(),
|
|
204
|
+
coverage: serviceCatalogCoverageSchema,
|
|
205
|
+
/**
|
|
206
|
+
* Entities the filter matched that did NOT become a service: no usable identity, or a name
|
|
207
|
+
* that yields no slug. Zero and "nothing matched" are told apart by {@link coverage}, and a
|
|
208
|
+
* non-zero value here is the only thing that explains a catalog thinner than the portal's
|
|
209
|
+
* own entity count.
|
|
210
|
+
*/
|
|
211
|
+
skippedServices: v.number(),
|
|
212
|
+
/**
|
|
213
|
+
* Portal services this pass REFUSED to write because the workspace already registers a service
|
|
214
|
+
* under that id by another route (an upload, a linked repo).
|
|
215
|
+
*
|
|
216
|
+
* Its own count rather than folded into {@link skippedServices}, because the two name opposite
|
|
217
|
+
* faults with opposite remedies: a skipped service is an entity the PORTAL describes in a way
|
|
218
|
+
* this platform cannot use, and this is a collision between two things the workspace itself
|
|
219
|
+
* did. Refusing is the only safe disposition, since overwriting would silently replace a
|
|
220
|
+
* hand-authored service (and its uploaded contracts, and any platform capability it was granted)
|
|
221
|
+
* with the portal's row, and disconnecting would then tombstone the original.
|
|
222
|
+
*/
|
|
223
|
+
skippedConflicts: v.number(),
|
|
224
|
+
/**
|
|
225
|
+
* API definitions a service DECLARES that this pass could not store: a type the platform
|
|
226
|
+
* serves no format for, an entity the portal would not return, an empty or oversized
|
|
227
|
+
* definition, or an id that collides with another interface of the same service. Reported
|
|
228
|
+
* rather than dropped, because a service listed with no interface reads to an agent as a
|
|
229
|
+
* service that publishes none.
|
|
230
|
+
*/
|
|
231
|
+
skippedApis: v.number(),
|
|
232
|
+
status: serviceCatalogSyncStatusSchema,
|
|
233
|
+
});
|
|
234
|
+
//# sourceMappingURL=service-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-catalog.js","sourceRoot":"","sources":["../src/service-catalog.ts"],"names":[],"mappings":"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"}
|