@backstage/backend-plugin-api 1.1.1-next.1 → 1.2.0-next.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/index.d.ts CHANGED
@@ -1,647 +1,541 @@
1
1
  /// <reference types="node" />
2
- import { Readable } from 'stream';
3
- import { Config } from '@backstage/config';
4
2
  import { JsonObject, HumanDuration, JsonValue } from '@backstage/types';
5
- import { Duration } from 'luxon';
6
- import { Handler, Request, Response } from 'express';
7
- import { PermissionAttributes, EvaluatorRequestOptions, PermissionEvaluator, AuthorizePermissionRequest, AuthorizePermissionResponse, QueryPermissionRequest, QueryPermissionResponse } from '@backstage/plugin-permission-common';
3
+ import { Request, Response, Handler } from 'express';
4
+ import { PermissionAttributes, EvaluatorRequestOptions, PermissionEvaluator, AuthorizePermissionRequest, AuthorizePermissionResponse, QueryPermissionRequest, QueryPermissionResponse, Permission } from '@backstage/plugin-permission-common';
8
5
  import { Knex } from 'knex';
6
+ import { PermissionRule } from '@backstage/plugin-permission-node';
7
+ import { Config } from '@backstage/config';
8
+ import { Duration } from 'luxon';
9
+ import { Readable } from 'stream';
9
10
  export { isChildPath } from '@backstage/cli-common';
10
11
 
11
12
  /**
12
- * A generic interface for fetching plain data from URLs.
13
- *
14
- * See the {@link https://backstage.io/docs/backend-system/core-services/url-reader | service documentation} for more details.
15
- *
13
+ * low (default): normal usage
14
+ * medium: accessing write endpoints
15
+ * high: non-root permission changes
16
+ * critical: root permission changes
16
17
  * @public
17
18
  */
18
- interface UrlReaderService {
19
- /**
20
- * Reads a single file and return its content.
21
- */
22
- readUrl(url: string, options?: UrlReaderServiceReadUrlOptions): Promise<UrlReaderServiceReadUrlResponse>;
19
+ type AuditorServiceEventSeverityLevel = 'low' | 'medium' | 'high' | 'critical';
20
+ /** @public */
21
+ type AuditorServiceCreateEventOptions = {
23
22
  /**
24
- * Reads a full or partial file tree.
23
+ * Use kebab-case to name audit events (e.g., "user-login", "file-download", "fetch"). Represents a logical group of similar events or operations. For example, "fetch" could be used as an eventId encompassing various fetch methods like "by-id" or "by-location".
24
+ *
25
+ * The `pluginId` already provides plugin/module context, so avoid redundant prefixes in the `eventId`.
25
26
  */
26
- readTree(url: string, options?: UrlReaderServiceReadTreeOptions): Promise<UrlReaderServiceReadTreeResponse>;
27
+ eventId: string;
28
+ /** (Optional) The severity level for the audit event. */
29
+ severityLevel?: AuditorServiceEventSeverityLevel;
30
+ /** (Optional) The associated HTTP request, if applicable. */
31
+ request?: Request<any, any, any, any, any>;
27
32
  /**
28
- * Searches for a file in a tree using a glob pattern.
33
+ * (Optional) Additional metadata relevant to the event, structured as a JSON object.
34
+ * This could include a `queryType` field, using kebab-case, for variations within the main event (e.g., "by-id", "by-user").
35
+ * For example, if the `eventId` is "fetch", the `queryType` in `meta` could be "by-id" or "by-location".
29
36
  */
30
- search(url: string, options?: UrlReaderServiceSearchOptions): Promise<UrlReaderServiceSearchResponse>;
37
+ meta?: JsonObject;
38
+ };
39
+ /** @public */
40
+ type AuditorServiceEvent = {
41
+ success(options?: {
42
+ meta?: JsonObject;
43
+ }): Promise<void>;
44
+ fail(options: {
45
+ meta?: JsonObject;
46
+ error: Error;
47
+ }): Promise<void>;
48
+ };
49
+ /**
50
+ * A service that provides an auditor facility.
51
+ *
52
+ * See the {@link https://backstage.io/docs/backend-system/core-services/auditor | service documentation} for more details.
53
+ *
54
+ * @public
55
+ */
56
+ interface AuditorService {
57
+ createEvent(options: AuditorServiceCreateEventOptions): Promise<AuditorServiceEvent>;
31
58
  }
59
+
32
60
  /**
33
- * An options object for readUrl operations.
61
+ * Represents a user principal (for example when a user Backstage token issued
62
+ * by the auth backend was given to a request).
63
+ *
64
+ * @remarks
65
+ *
66
+ * Additional information about the user can be fetched using the
67
+ * {@link UserInfoService}.
34
68
  *
35
69
  * @public
36
70
  */
37
- type UrlReaderServiceReadUrlOptions = {
38
- /**
39
- * An ETag which can be provided to check whether a
40
- * {@link UrlReaderService.readUrl} response has changed from a previous execution.
41
- *
42
- * @remarks
43
- *
44
- * In the {@link UrlReaderService.readUrl} response, an ETag is returned along with
45
- * the data. The ETag is a unique identifier of the data, usually the commit
46
- * SHA or ETag from the target.
47
- *
48
- * When an ETag is given in ReadUrlOptions, {@link UrlReaderService.readUrl} will
49
- * first compare the ETag against the ETag of the target. If they match,
50
- * {@link UrlReaderService.readUrl} will throw a
51
- * {@link @backstage/errors#NotModifiedError} indicating that the response
52
- * will not differ from the previous response which included this particular
53
- * ETag. If they do not match, {@link UrlReaderService.readUrl} will return the rest
54
- * of the response along with a new ETag.
55
- */
56
- etag?: string;
57
- /**
58
- * A date which can be provided to check whether a
59
- * {@link UrlReaderService.readUrl} response has changed since the lastModifiedAt.
60
- *
61
- * @remarks
62
- *
63
- * In the {@link UrlReaderService.readUrl} response, an lastModifiedAt is returned
64
- * along with data. The lastModifiedAt date represents the last time the data
65
- * was modified.
66
- *
67
- * When an lastModifiedAfter is given in ReadUrlOptions, {@link UrlReaderService.readUrl}
68
- * will compare the lastModifiedAfter against the lastModifiedAt of the target. If
69
- * the data has not been modified since this date, the {@link UrlReaderService.readUrl}
70
- * will throw a {@link @backstage/errors#NotModifiedError} indicating that the
71
- * response does not contain any new data. If they do not match,
72
- * {@link UrlReaderService.readUrl} will return the rest of the response along with new
73
- * lastModifiedAt date.
74
- */
75
- lastModifiedAfter?: Date;
76
- /**
77
- * An abort signal to pass down to the underlying request.
78
- *
79
- * @remarks
80
- *
81
- * Not all reader implementations may take this field into account.
82
- */
83
- signal?: AbortSignal;
71
+ type BackstageUserPrincipal = {
72
+ type: 'user';
84
73
  /**
85
- * An optional token to use for authentication when reading the resources.
86
- *
87
- * @remarks
88
- *
89
- * By default all URL Readers will use the integrations config which is supplied
90
- * when creating the Readers. Sometimes it might be desireable to use the already
91
- * created URLReaders but with a different token, maybe that's supplied by the user
92
- * at runtime.
74
+ * The entity ref of the user entity that this principal represents.
93
75
  */
94
- token?: string;
76
+ userEntityRef: string;
95
77
  };
96
78
  /**
97
- * A response object for {@link UrlReaderService.readUrl} operations.
79
+ * Represents a principal that is not authenticated (for example when no token
80
+ * at all was given to a request).
98
81
  *
99
82
  * @public
100
83
  */
101
- type UrlReaderServiceReadUrlResponse = {
102
- /**
103
- * Returns the data that was read from the remote URL.
104
- */
105
- buffer(): Promise<Buffer>;
84
+ type BackstageNonePrincipal = {
85
+ type: 'none';
86
+ };
87
+ /**
88
+ * Represents a service principal (for example when an external access method
89
+ * token was given to a request, or the caller was a Backstage backend plugin).
90
+ * @public
91
+ */
92
+ type BackstageServicePrincipal = {
93
+ type: 'service';
106
94
  /**
107
- * Returns the data that was read from the remote URL as a Readable stream.
95
+ * A string that represents the service.
108
96
  *
109
97
  * @remarks
110
98
  *
111
- * This method will be required in a future release.
99
+ * This string is only informational, has no well defined semantics, and
100
+ * should never be used to drive actual logic in code.
112
101
  */
113
- stream?(): Readable;
102
+ subject: string;
114
103
  /**
115
- * Etag returned by content provider.
104
+ * The access restrictions that apply to this principal.
116
105
  *
117
106
  * @remarks
118
107
  *
119
- * Can be used to compare and cache responses when doing subsequent calls.
120
- */
121
- etag?: string;
122
- /**
123
- * Last modified date of the file contents.
108
+ * If no access restrictions are provided the principal is assumed to have
109
+ * unlimited access, at a framework level. The permissions system and
110
+ * individual plugins may or may not still apply additional access controls on
111
+ * top of this.
124
112
  */
125
- lastModifiedAt?: Date;
113
+ accessRestrictions?: BackstagePrincipalAccessRestrictions;
126
114
  };
127
115
  /**
128
- * An options object for {@link UrlReaderService.readTree} operations.
116
+ * The access restrictions that apply to a given principal.
129
117
  *
130
118
  * @public
131
119
  */
132
- type UrlReaderServiceReadTreeOptions = {
120
+ type BackstagePrincipalAccessRestrictions = {
133
121
  /**
134
- * A filter that can be used to select which files should be included.
135
- *
136
- * @remarks
137
- *
138
- * The path passed to the filter function is the relative path from the URL
139
- * that the file tree is fetched from, without any leading '/'.
122
+ * If given, the principal is limited to only performing actions with these
123
+ * named permissions.
140
124
  *
141
- * For example, given the URL https://github.com/my/repo/tree/master/my-dir, a file
142
- * at https://github.com/my/repo/blob/master/my-dir/my-subdir/my-file.txt will
143
- * be represented as my-subdir/my-file.txt
125
+ * Note that this only applies where permissions checks are enabled in the
126
+ * first place. Endpoints that are not protected by the permissions system at
127
+ * all, are not affected by this setting.
144
128
  *
145
- * If no filter is provided, all files are extracted.
129
+ * This array always has at least one element, or is missing entirely.
146
130
  */
147
- filter?(path: string, info?: {
148
- size: number;
149
- }): boolean;
131
+ permissionNames?: string[];
150
132
  /**
151
- * An ETag which can be provided to check whether a
152
- * {@link UrlReaderService.readTree} response has changed from a previous execution.
153
- *
154
- * @remarks
133
+ * If given, the principal is limited to only performing actions whose
134
+ * permissions have these attributes.
155
135
  *
156
- * In the {@link UrlReaderService.readTree} response, an ETag is returned along with
157
- * the tree blob. The ETag is a unique identifier of the tree blob, usually
158
- * the commit SHA or ETag from the target.
136
+ * Note that this only applies where permissions checks are enabled in the
137
+ * first place. Endpoints that are not protected by the permissions system at
138
+ * all, are not affected by this setting.
159
139
  *
160
- * When an ETag is given as a request option, {@link UrlReaderService.readTree} will
161
- * first compare the ETag against the ETag on the target branch. If they
162
- * match, {@link UrlReaderService.readTree} will throw a
163
- * {@link @backstage/errors#NotModifiedError} indicating that the response
164
- * will not differ from the previous response which included this particular
165
- * ETag. If they do not match, {@link UrlReaderService.readTree} will return the
166
- * rest of the response along with a new ETag.
140
+ * This object always has at least one key, or is missing entirely.
167
141
  */
168
- etag?: string;
142
+ permissionAttributes?: {
143
+ /**
144
+ * Match any of these action values. This array always has at least one
145
+ * element, or is missing entirely.
146
+ */
147
+ action?: Array<Required<PermissionAttributes>['action']>;
148
+ };
149
+ };
150
+ /**
151
+ * An opaque representation of credentials, for example as passed in a
152
+ * request-response flow.
153
+ *
154
+ * @public
155
+ */
156
+ type BackstageCredentials<TPrincipal = unknown> = {
157
+ $$type: '@backstage/BackstageCredentials';
169
158
  /**
170
- * An abort signal to pass down to the underlying request.
171
- *
172
- * @remarks
173
- *
174
- * Not all reader implementations may take this field into account.
159
+ * If the credentials have a limited lifetime, this is the time at which they
160
+ * expire and may no longer be accepted by a receiver.
175
161
  */
176
- signal?: AbortSignal;
162
+ expiresAt?: Date;
177
163
  /**
178
- * An optional token to use for authentication when reading the resources.
164
+ * The principal (originator) of the request.
179
165
  *
180
166
  * @remarks
181
167
  *
182
- * By default all URL Readers will use the integrations config which is supplied
183
- * when creating the Readers. Sometimes it might be desireable to use the already
184
- * created URLReaders but with a different token, maybe that's supplied by the user
185
- * at runtime.
168
+ * This is semantically the originator of a request chain, and may or may not
169
+ * represent the immediate caller of your service. For example, in
170
+ * on-behalf-of scenarios, the immediate caller may be an intermediary backend
171
+ * service, but the principal may still be a user that was the original
172
+ * caller.
186
173
  */
187
- token?: string;
174
+ principal: TPrincipal;
188
175
  };
189
176
  /**
190
- * Options that control {@link UrlReaderServiceReadTreeResponse.dir} execution.
177
+ * The types of principal that can be represented in a
178
+ * {@link BackstageCredentials} object.
191
179
  *
192
180
  * @public
193
181
  */
194
- type UrlReaderServiceReadTreeResponseDirOptions = {
195
- /**
196
- * The directory to write files to.
197
- *
198
- * @remarks
199
- *
200
- * Defaults to the OS tmpdir, or `backend.workingDirectory` if set in config.
201
- */
202
- targetDir?: string;
182
+ type BackstagePrincipalTypes = {
183
+ user: BackstageUserPrincipal;
184
+ service: BackstageServicePrincipal;
185
+ none: BackstageNonePrincipal;
186
+ unknown: unknown;
203
187
  };
204
188
  /**
205
- * A response object for {@link UrlReaderService.readTree} operations.
189
+ * Provides token authentication and credentials management.
190
+ *
191
+ * See the {@link https://backstage.io/docs/backend-system/core-services/auth | service documentation} for more details.
206
192
  *
207
193
  * @public
208
194
  */
209
- type UrlReaderServiceReadTreeResponse = {
195
+ interface AuthService {
210
196
  /**
211
- * Returns an array of all the files inside the tree, and corresponding
212
- * functions to read their content.
197
+ * Verifies a token and returns the associated credentials.
213
198
  */
214
- files(): Promise<UrlReaderServiceReadTreeResponseFile[]>;
199
+ authenticate(token: string, options?: {
200
+ /**
201
+ * If set to true, allow limited access tokens (such as cookies).
202
+ *
203
+ * If this flag is not set, or is set to false, calls with limited access
204
+ * tokens will lead to a {@link @backstage/errors#NotAllowedError} being
205
+ * thrown.
206
+ */
207
+ allowLimitedAccess?: boolean;
208
+ }): Promise<BackstageCredentials>;
215
209
  /**
216
- * Returns the tree contents as a binary archive, using a stream.
210
+ * Checks if the given credentials are of the given type, and narrows the
211
+ * TypeScript type accordingly if there's a match.
217
212
  */
218
- archive(): Promise<NodeJS.ReadableStream>;
213
+ isPrincipal<TType extends keyof BackstagePrincipalTypes>(credentials: BackstageCredentials, type: TType): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]>;
219
214
  /**
220
- * Extracts the tree response into a directory and returns the path of the
221
- * directory.
222
- *
223
- * **NOTE**: It is the responsibility of the caller to remove the directory after use.
215
+ * Create a credentials object that represents an unauthenticated caller.
224
216
  */
225
- dir(options?: UrlReaderServiceReadTreeResponseDirOptions): Promise<string>;
217
+ getNoneCredentials(): Promise<BackstageCredentials<BackstageNonePrincipal>>;
226
218
  /**
227
- * Etag returned by content provider.
219
+ * Create a credentials object that represents the current service itself.
220
+ */
221
+ getOwnServiceCredentials(): Promise<BackstageCredentials<BackstageServicePrincipal>>;
222
+ /**
223
+ * Issue a token that can be used for authenticating calls towards other
224
+ * backend plugins.
228
225
  *
229
226
  * @remarks
230
227
  *
231
- * Can be used to compare and cache responses when doing subsequent calls.
228
+ * This method should be called before each request. Do not hold on to the
229
+ * issued token and reuse it for future calls.
232
230
  */
233
- etag: string;
234
- };
235
- /**
236
- * Represents a single file in a {@link UrlReaderService.readTree} response.
237
- *
238
- * @public
239
- */
240
- type UrlReaderServiceReadTreeResponseFile = {
231
+ getPluginRequestToken(options: {
232
+ /**
233
+ * The credentials of the originator of the request.
234
+ *
235
+ * @remarks
236
+ *
237
+ * This is most commonly the result of
238
+ * {@link AuthService.getOwnServiceCredentials} when the current service is
239
+ * the originator, or the output of {@link HttpAuthService.credentials} when
240
+ * performing requests on behalf of an incoming request identity.
241
+ */
242
+ onBehalfOf: BackstageCredentials;
243
+ /**
244
+ * The ID of the plugin that the request is being made to.
245
+ */
246
+ targetPluginId: string;
247
+ }): Promise<{
248
+ token: string;
249
+ }>;
241
250
  /**
242
- * The filepath of the data.
251
+ * Issue a limited user token that can be used e.g. in cookie flows.
243
252
  */
244
- path: string;
253
+ getLimitedUserToken(
245
254
  /**
246
- * The binary contents of the file.
255
+ * The credentials that this token should represent. Must be a user
256
+ * principal. Commonly the output of {@link HttpAuthService.credentials} is
257
+ * used as the input.
247
258
  */
248
- content(): Promise<Buffer>;
259
+ credentials: BackstageCredentials<BackstageUserPrincipal>): Promise<{
260
+ token: string;
261
+ expiresAt: Date;
262
+ }>;
249
263
  /**
250
- * The last modified timestamp of the data.
264
+ * Retrieve the public keys that have been used to sign tokens that were
265
+ * issued by this service. This list is periodically pruned from keys that are
266
+ * significantly past their expiry.
251
267
  */
252
- lastModifiedAt?: Date;
253
- };
268
+ listPublicServiceKeys(): Promise<{
269
+ keys: JsonObject[];
270
+ }>;
271
+ }
272
+
254
273
  /**
255
- * An options object for search operations.
274
+ * Options passed to {@link CacheService.set}.
256
275
  *
257
276
  * @public
258
277
  */
259
- type UrlReaderServiceSearchOptions = {
260
- /**
261
- * An etag can be provided to check whether the search response has changed from a previous execution.
262
- *
263
- * In the search() response, an etag is returned along with the files. The etag is a unique identifier
264
- * of the current tree, usually the commit SHA or etag from the target.
265
- *
266
- * When an etag is given in SearchOptions, search will first compare the etag against the etag
267
- * on the target branch. If they match, search will throw a NotModifiedError indicating that the search
268
- * response will not differ from the previous response which included this particular etag. If they mismatch,
269
- * search will return the rest of SearchResponse along with a new etag.
270
- */
271
- etag?: string;
272
- /**
273
- * An abort signal to pass down to the underlying request.
274
- *
275
- * @remarks
276
- *
277
- * Not all reader implementations may take this field into account.
278
- */
279
- signal?: AbortSignal;
278
+ type CacheServiceSetOptions = {
280
279
  /**
281
- * An optional token to use for authentication when reading the resources.
282
- *
283
- * @remarks
284
- *
285
- * By default all URL Readers will use the integrations config which is supplied
286
- * when creating the Readers. Sometimes it might be desireable to use the already
287
- * created URLReaders but with a different token, maybe that's supplied by the user
288
- * at runtime.
280
+ * Optional TTL (in milliseconds if given as a number). Defaults to the TTL provided when the client
281
+ * was set up (or no TTL if none are provided).
289
282
  */
290
- token?: string;
283
+ ttl?: number | HumanDuration;
291
284
  };
292
285
  /**
293
- * The output of a search operation.
286
+ * Options passed to {@link CacheService.withOptions}.
294
287
  *
295
288
  * @public
296
289
  */
297
- type UrlReaderServiceSearchResponse = {
298
- /**
299
- * The files that matched the search query.
300
- */
301
- files: UrlReaderServiceSearchResponseFile[];
290
+ type CacheServiceOptions = {
302
291
  /**
303
- * A unique identifier of the current remote tree, usually the commit SHA or etag from the target.
292
+ * An optional default TTL (in milliseconds if given as a number) to be set when getting a client
293
+ * instance. If not provided, data will persist indefinitely by default (or
294
+ * can be configured per entry at set-time).
304
295
  */
305
- etag: string;
296
+ defaultTtl?: number | HumanDuration;
306
297
  };
307
298
  /**
308
- * Represents a single file in a search response.
299
+ * A pre-configured, storage agnostic cache service suitable for use by
300
+ * Backstage plugins.
301
+ *
302
+ * See the {@link https://backstage.io/docs/backend-system/core-services/cache | service documentation} for more details.
309
303
  *
310
304
  * @public
311
305
  */
312
- type UrlReaderServiceSearchResponseFile = {
306
+ interface CacheService {
313
307
  /**
314
- * The full URL to the file.
308
+ * Reads data from a cache store for the given key. If no data was found,
309
+ * returns undefined.
315
310
  */
316
- url: string;
311
+ get<TValue extends JsonValue>(key: string): Promise<TValue | undefined>;
317
312
  /**
318
- * The binary contents of the file.
313
+ * Writes the given data to a cache store, associated with the given key. An
314
+ * optional TTL may also be provided, otherwise it defaults to the TTL that
315
+ * was provided when the client was instantiated.
319
316
  */
320
- content(): Promise<Buffer>;
317
+ set(key: string, value: JsonValue, options?: CacheServiceSetOptions): Promise<void>;
321
318
  /**
322
- * The last modified timestamp of the data.
319
+ * Removes the given key from the cache store.
323
320
  */
324
- lastModifiedAt?: Date;
325
- };
321
+ delete(key: string): Promise<void>;
322
+ /**
323
+ * Creates a new {@link CacheService} instance with the given options.
324
+ */
325
+ withOptions(options: CacheServiceOptions): CacheService;
326
+ }
326
327
 
327
328
  /**
328
- * A function that can be called as a scheduled task.
329
+ * Manages access to databases that plugins get.
329
330
  *
330
- * It may optionally accept an abort signal argument. When the signal triggers,
331
- * processing should abort and return as quickly as possible.
331
+ * See the {@link https://backstage.io/docs/backend-system/core-services/database | service documentation} for more details.
332
332
  *
333
333
  * @public
334
334
  */
335
- type SchedulerServiceTaskFunction = ((abortSignal: AbortSignal) => void | Promise<void>) | (() => void | Promise<void>);
335
+ interface DatabaseService {
336
+ /**
337
+ * getClient provides backend plugins database connections for itself.
338
+ *
339
+ * The purpose of this method is to allow plugins to get isolated data
340
+ * stores so that plugins are discouraged from database integration.
341
+ */
342
+ getClient(): Promise<Knex>;
343
+ /**
344
+ * This property is used to control the behavior of database migrations.
345
+ */
346
+ migrations?: {
347
+ /**
348
+ * skip database migrations. Useful if connecting to a read-only database.
349
+ *
350
+ * @defaultValue false
351
+ */
352
+ skip?: boolean;
353
+ };
354
+ }
355
+
336
356
  /**
337
- * A semi-opaque type to describe an actively scheduled task.
357
+ * The DiscoveryService is used to provide a mechanism for backend
358
+ * plugins to discover the endpoints for itself or other backend plugins.
359
+ *
360
+ * See the {@link https://backstage.io/docs/backend-system/core-services/discovery | service documentation} for more details.
361
+ *
362
+ * @remarks
363
+ *
364
+ * The purpose of the discovery API is to allow for many different deployment
365
+ * setups and routing methods through a central configuration, instead
366
+ * of letting each individual plugin manage that configuration.
367
+ *
368
+ * Implementations of the discovery API can be as simple as a URL pattern
369
+ * using the pluginId, but could also have overrides for individual plugins,
370
+ * or query a separate discovery service.
338
371
  *
339
372
  * @public
340
373
  */
341
- type SchedulerServiceTaskDescriptor = {
342
- /**
343
- * The unique identifier of the task.
344
- */
345
- id: string;
374
+ interface DiscoveryService {
346
375
  /**
347
- * The scope of the task.
376
+ * Returns the internal HTTP base URL for a given plugin, without a trailing slash.
377
+ *
378
+ * @remarks
379
+ *
380
+ * The returned URL should point to an internal endpoint for the plugin, with
381
+ * the shortest route possible. The URL should be used for service-to-service
382
+ * communication within a Backstage backend deployment.
383
+ *
384
+ * This method must always be called just before making each request, as opposed to
385
+ * fetching the URL once when constructing an API client. That is to ensure that more
386
+ * flexible routing patterns can be supported where a different result might be returned each time.
387
+ *
388
+ * For example, asking for the URL for `catalog` may return something
389
+ * like `http://10.1.2.3/api/catalog`
348
390
  */
349
- scope: 'global' | 'local';
391
+ getBaseUrl(pluginId: string): Promise<string>;
350
392
  /**
351
- * The settings that control the task flow. This is a semi-opaque structure
352
- * that is mainly there for debugging purposes. Do not make any assumptions
353
- * about the contents of this field.
393
+ * Returns the external HTTP base backend URL for a given plugin, without a trailing slash.
394
+ *
395
+ * @remarks
396
+ *
397
+ * The returned URL should point to an external endpoint for the plugin, such that
398
+ * it is reachable from the Backstage frontend and other external services. The returned
399
+ * URL should be usable for example as a callback / webhook URL.
400
+ *
401
+ * The returned URL should be stable and in general not change unless other static
402
+ * or external configuration is changed. Changes should not come as a surprise
403
+ * to an operator of the Backstage backend.
404
+ *
405
+ * For example, asking for the URL for `catalog` may return something
406
+ * like `https://backstage.example.com/api/catalog`
354
407
  */
355
- settings: {
356
- version: number;
357
- } & JsonObject;
358
- };
408
+ getExternalBaseUrl(pluginId: string): Promise<string>;
409
+ }
410
+
359
411
  /**
360
- * Options that control the scheduling of a task.
412
+ * Provides handling of credentials in an ongoing request.
413
+ *
414
+ * See the {@link https://backstage.io/docs/backend-system/core-services/http-auth | service documentation} for more details.
361
415
  *
362
416
  * @public
363
417
  */
364
- interface SchedulerServiceTaskScheduleDefinition {
418
+ interface HttpAuthService {
365
419
  /**
366
- * How often you want the task to run. The system does its best to avoid
367
- * overlapping invocations.
420
+ * Extracts the caller's credentials from a request.
368
421
  *
369
422
  * @remarks
370
423
  *
371
- * This is the best effort value; under some circumstances there can be
372
- * deviations. For example, if the task runtime is longer than the frequency
373
- * and the timeout has not been given or not been exceeded yet, the next
374
- * invocation of this task will be delayed until after the previous one
375
- * finishes.
376
- *
377
- * This is a required field.
378
- */
379
- frequency: {
380
- /**
381
- * A crontab style string.
382
- *
383
- * @remarks
384
- *
385
- * Overview:
386
- *
387
- * ```
388
- * ┌────────────── second (optional)
389
- * │ ┌──────────── minute
390
- * │ │ ┌────────── hour
391
- * │ │ │ ┌──────── day of month
392
- * │ │ │ │ ┌────── month
393
- * │ │ │ │ │ ┌──── day of week
394
- * │ │ │ │ │ │
395
- * │ │ │ │ │ │
396
- * * * * * * *
397
- * ```
398
- */
399
- cron: string;
400
- } | Duration | HumanDuration | {
401
- trigger: 'manual';
402
- };
403
- /**
404
- * The maximum amount of time that a single task invocation can take, before
405
- * it's considered timed out and gets "released" such that a new invocation
406
- * is permitted to take place (possibly, then, on a different worker).
407
- */
408
- timeout: Duration | HumanDuration;
409
- /**
410
- * The amount of time that should pass before the first invocation happens.
411
- *
412
- * @remarks
424
+ * The credentials have been validated before returning, and are guaranteed to
425
+ * adhere to whatever policies have been added to this route using
426
+ * {@link HttpRouterService.addAuthPolicy}, if any.
413
427
  *
414
- * This can be useful in cold start scenarios to stagger or delay some heavy
415
- * compute jobs. If no value is given for this field then the first invocation
416
- * will happen as soon as possible according to the cadence.
428
+ * Further restrictions can be imposed by passing in options that control the
429
+ * allowed types of credential.
417
430
  *
418
- * NOTE: This is a per-worker delay. If you have a cluster of workers all
419
- * collaborating on a task that has its `scope` field set to `'global'`, then
420
- * you may still see the task being processed by other long-lived workers,
421
- * while any given single worker is in its initial sleep delay time e.g. after
422
- * a deployment. Therefore, this parameter is not useful for "globally" pausing
423
- * work; its main intended use is for individual machines to get a chance to
424
- * reach some equilibrium at startup before triggering heavy batch workloads.
431
+ * You can narrow the returned credentials object to specific principal types
432
+ * using {@link AuthService.isPrincipal}.
425
433
  */
426
- initialDelay?: Duration | HumanDuration;
434
+ credentials<TAllowed extends keyof BackstagePrincipalTypes = 'unknown'>(
427
435
  /**
428
- * Sets the scope of concurrency control / locking to apply for invocations of
429
- * this task.
430
- *
431
- * @remarks
432
- *
433
- * When the scope is set to the default value `'global'`, the scheduler will
434
- * attempt to ensure that only one worker machine runs the task at a time,
435
- * according to the given cadence. This means that as the number of worker
436
- * hosts increases, the invocation frequency of this task will not go up.
437
- * Instead, the load is spread randomly across hosts. This setting is useful
438
- * for tasks that access shared resources, for example catalog ingestion tasks
439
- * where you do not want many machines to repeatedly import the same data and
440
- * trample over each other.
441
- *
442
- * When the scope is set to `'local'`, there is no concurrency control across
443
- * hosts. Each host runs the task according to the given cadence similarly to
444
- * `setInterval`, but the runtime ensures that there are no overlapping runs.
445
- *
446
- * @defaultValue 'global'
436
+ * An Express request object.
447
437
  */
448
- scope?: 'global' | 'local';
449
- }
450
- /**
451
- * Config options for {@link SchedulerServiceTaskScheduleDefinition}
452
- * that control the scheduling of a task.
453
- *
454
- * @public
455
- */
456
- interface SchedulerServiceTaskScheduleDefinitionConfig {
438
+ req: Request<any, any, any, any, any>,
457
439
  /**
458
- * How often you want the task to run. The system does its best to avoid
459
- * overlapping invocations.
460
- *
461
- * @remarks
462
- *
463
- * This is the best effort value; under some circumstances there can be
464
- * deviations. For example, if the task runtime is longer than the frequency
465
- * and the timeout has not been given or not been exceeded yet, the next
466
- * invocation of this task will be delayed until after the previous one
467
- * finishes.
468
- *
469
- * This is a required field.
440
+ * Optional further restrictions.
470
441
  */
471
- frequency: {
442
+ options?: {
472
443
  /**
473
- * A crontab style string.
444
+ * If specified, allow only principals of the given type(s).
474
445
  *
475
- * @remarks
446
+ * If the incoming credentials were not of a type that matched this
447
+ * restriction, a {@link @backstage/errors#NotAllowedError} is thrown.
476
448
  *
477
- * Overview:
449
+ * The default is to allow user and service principals.
450
+ */
451
+ allow?: Array<TAllowed>;
452
+ /**
453
+ * If set to true, allow limited access tokens (such as cookies).
478
454
  *
479
- * ```
480
- * ┌────────────── second (optional)
481
- * ┌──────────── minute
482
- * │ │ ┌────────── hour
483
- * │ │ │ ┌──────── day of month
484
- * │ │ │ │ ┌────── month
485
- * │ │ │ │ │ ┌──── day of week
486
- * │ │ │ │ │ │
487
- * │ │ │ │ │ │
488
- * * * * * * *
489
- * ```
455
+ * If this flag is not set, or is set to false, calls with limited access
456
+ * tokens will lead to a {@link @backstage/errors#NotAllowedError} being
457
+ * thrown.
490
458
  */
491
- cron: string;
492
- } | string | HumanDuration
493
- /**
494
- * This task will only run when manually triggered with the `triggerTask` method; no automatic
495
- * scheduling. This is useful for locking of global tasks that should not be run concurrently.
496
- */
497
- | {
498
- trigger: 'manual';
499
- };
500
- /**
501
- * The maximum amount of time that a single task invocation can take, before
502
- * it's considered timed out and gets "released" such that a new invocation
503
- * is permitted to take place (possibly, then, on a different worker).
504
- */
505
- timeout: string | HumanDuration;
506
- /**
507
- * The amount of time that should pass before the first invocation happens.
508
- *
509
- * @remarks
510
- *
511
- * This can be useful in cold start scenarios to stagger or delay some heavy
512
- * compute jobs. If no value is given for this field then the first invocation
513
- * will happen as soon as possible according to the cadence.
514
- *
515
- * NOTE: This is a per-worker delay. If you have a cluster of workers all
516
- * collaborating on a task that has its `scope` field set to `'global'`, then
517
- * you may still see the task being processed by other long-lived workers,
518
- * while any given single worker is in its initial sleep delay time e.g. after
519
- * a deployment. Therefore, this parameter is not useful for "globally" pausing
520
- * work; its main intended use is for individual machines to get a chance to
521
- * reach some equilibrium at startup before triggering heavy batch workloads.
522
- */
523
- initialDelay?: string | HumanDuration;
459
+ allowLimitedAccess?: boolean;
460
+ }): Promise<BackstageCredentials<BackstagePrincipalTypes[TAllowed]>>;
524
461
  /**
525
- * Sets the scope of concurrency control / locking to apply for invocations of
526
- * this task.
527
- *
528
- * @remarks
529
- *
530
- * When the scope is set to the default value `'global'`, the scheduler will
531
- * attempt to ensure that only one worker machine runs the task at a time,
532
- * according to the given cadence. This means that as the number of worker
533
- * hosts increases, the invocation frequency of this task will not go up.
534
- * Instead, the load is spread randomly across hosts. This setting is useful
535
- * for tasks that access shared resources, for example catalog ingestion tasks
536
- * where you do not want many machines to repeatedly import the same data and
537
- * trample over each other.
538
- *
539
- * When the scope is set to `'local'`, there is no concurrency control across
540
- * hosts. Each host runs the task according to the given cadence similarly to
541
- * `setInterval`, but the runtime ensures that there are no overlapping runs.
462
+ * Issues a limited access token as a cookie on the given response object.
463
+ * This is only possible for requests that were originally made with user
464
+ * credentials (such as a Backstage token).
542
465
  *
543
- * @defaultValue 'global'
544
- */
545
- scope?: 'global' | 'local';
546
- }
547
- /**
548
- * Options that apply to the invocation of a given task.
549
- *
550
- * @public
551
- */
552
- interface SchedulerServiceTaskInvocationDefinition {
553
- /**
554
- * A unique ID (within the scope of the plugin) for the task.
466
+ * This must be called before sending any payload data.
555
467
  */
556
- id: string;
468
+ issueUserCookie(
557
469
  /**
558
- * The actual task function to be invoked regularly.
470
+ * An Express response object.
559
471
  */
560
- fn: SchedulerServiceTaskFunction;
472
+ res: Response,
561
473
  /**
562
- * An abort signal that, when triggered, will stop the recurring execution of
563
- * the task.
474
+ * Optional further settings.
564
475
  */
565
- signal?: AbortSignal;
476
+ options?: {
477
+ /**
478
+ * Issue the cookie for this specific credential. Must be a "user" type
479
+ * principal, or a "none" type (which leads to deleting the cookie).
480
+ *
481
+ * @remarks
482
+ *
483
+ * Normally you do not have to specify this option, because the default
484
+ * behavior is to extract the credentials from the request that
485
+ * corresponded to the given respnse.
486
+ */
487
+ credentials?: BackstageCredentials;
488
+ }): Promise<{
489
+ expiresAt: Date;
490
+ }>;
566
491
  }
492
+
567
493
  /**
568
- * A previously prepared task schedule, ready to be invoked.
494
+ * Options for {@link HttpRouterService.addAuthPolicy}.
569
495
  *
570
496
  * @public
571
497
  */
572
- interface SchedulerServiceTaskRunner {
573
- /**
574
- * Takes the schedule and executes an actual task using it.
575
- *
576
- * @param task - The actual runtime properties of the task
577
- */
578
- run(task: SchedulerServiceTaskInvocationDefinition): Promise<void>;
498
+ interface HttpRouterServiceAuthPolicy {
499
+ path: string;
500
+ allow: 'unauthenticated' | 'user-cookie';
579
501
  }
580
502
  /**
581
- * Deals with the scheduling of distributed tasks, for a given plugin.
503
+ * Allows plugins to register HTTP routes.
582
504
  *
583
- * See the {@link https://backstage.io/docs/backend-system/core-services/scheduler | service documentation} for more details.
505
+ * See the {@link https://backstage.io/docs/backend-system/core-services/http-router | service documentation} for more details.
584
506
  *
585
507
  * @public
586
508
  */
587
- interface SchedulerService {
588
- /**
589
- * Manually triggers a task by ID.
590
- *
591
- * If the task doesn't exist, a NotFoundError is thrown. If the task is
592
- * currently running, a ConflictError is thrown.
593
- *
594
- * @param id - The task ID
595
- */
596
- triggerTask(id: string): Promise<void>;
509
+ interface HttpRouterService {
597
510
  /**
598
- * Schedules a task function for recurring runs.
599
- *
600
- * @remarks
601
- *
602
- * The `scope` task field controls whether to use coordinated exclusive
603
- * invocation across workers, or to just coordinate within the current worker.
604
- *
605
- * This convenience method performs both the scheduling and invocation in one
606
- * go.
607
- *
608
- * @param task - The task definition
511
+ * Registers an Express request handler under the plugin's base router. This
512
+ * typically makes its base path `/api/<plugin-id>`.
609
513
  */
610
- scheduleTask(task: SchedulerServiceTaskScheduleDefinition & SchedulerServiceTaskInvocationDefinition): Promise<void>;
514
+ use(handler: Handler): void;
611
515
  /**
612
- * Creates a scheduled but dormant recurring task, ready to be launched at a
613
- * later time.
516
+ * Adds an auth policy to the router. This is used to allow unauthenticated or
517
+ * cookie based access to parts of a plugin's API.
614
518
  *
615
519
  * @remarks
616
520
  *
617
- * This method is useful for pre-creating a schedule in outer code to be
618
- * passed into an inner implementation, such that the outer code controls
619
- * scheduling while inner code controls implementation.
620
- *
621
- * @param schedule - The task schedule
622
- */
623
- createScheduledTaskRunner(schedule: SchedulerServiceTaskScheduleDefinition): SchedulerServiceTaskRunner;
624
- /**
625
- * Returns all scheduled tasks registered to this scheduler.
521
+ * The paths given follow the same pattern as the routers given to the `use`
522
+ * method, that is, they are relative to the plugin's base URL, and can
523
+ * contain placeholders.
626
524
  *
627
- * @remarks
525
+ * @example
628
526
  *
629
- * This method is useful for triggering tasks manually using the triggerTask
630
- * functionality. Note that the returned tasks contain only tasks that have
631
- * been initialized in this instance of the scheduler.
527
+ * ```ts
528
+ * http.addAuthPolicy({
529
+ * path: '/static/:id',
530
+ * allow: 'user-cookie',
531
+ * });
532
+ * ```
632
533
  *
633
- * @returns Scheduled tasks
534
+ * This allows limited access tokens via cookies on the
535
+ * `/api/<plugin-id>/static/*` paths, but not unauthenticated access.
634
536
  */
635
- getScheduledTasks(): Promise<SchedulerServiceTaskDescriptor[]>;
537
+ addAuthPolicy(policy: HttpRouterServiceAuthPolicy): void;
636
538
  }
637
- /**
638
- * Reads a {@link SchedulerServiceTaskScheduleDefinition} from config. Expects
639
- * the config not to be the root config, but the config for the definition.
640
- *
641
- * @param config - config for a TaskScheduleDefinition.
642
- * @public
643
- */
644
- declare function readSchedulerServiceTaskScheduleDefinitionFromConfig(config: Config): SchedulerServiceTaskScheduleDefinition;
645
539
 
646
540
  /**
647
541
  * A service that provides a logging facility.
@@ -658,16 +552,6 @@ interface LoggerService {
658
552
  child(meta: JsonObject): LoggerService;
659
553
  }
660
554
 
661
- /**
662
- * Root-level logging.
663
- *
664
- * See the {@link https://backstage.io/docs/backend-system/core-services/root-logger | service documentation} for more details.
665
- *
666
- * @public
667
- */
668
- interface RootLoggerService extends LoggerService {
669
- }
670
-
671
555
  /**
672
556
  * @public
673
557
  */
@@ -719,29 +603,148 @@ interface LifecycleService {
719
603
  }
720
604
 
721
605
  /**
722
- * Registration of backend startup and shutdown lifecycle hooks.
606
+ * Options for {@link PermissionsService} requests.
723
607
  *
724
- * See the {@link https://backstage.io/docs/backend-system/core-services/root-lifecycle | service documentation} for more details.
608
+ * @public
609
+ */
610
+ interface PermissionsServiceRequestOptions extends EvaluatorRequestOptions {
611
+ credentials: BackstageCredentials;
612
+ }
613
+ /**
614
+ * Permission system integration for authorization of user/service actions.
615
+ *
616
+ * See the {@link https://backstage.io/docs/permissions/overview | permissions documentation}
617
+ * and the {@link https://backstage.io/docs/backend-system/core-services/permissions | service documentation}
618
+ * for more details.
725
619
  *
726
620
  * @public
727
621
  */
728
- interface RootLifecycleService extends LifecycleService {
729
- addBeforeShutdownHook(hook: () => void | Promise<void>): void;
622
+ interface PermissionsService extends PermissionEvaluator {
623
+ /**
624
+ * Evaluates
625
+ * {@link @backstage/plugin-permission-common#Permission | Permissions} and
626
+ * returns definitive decisions.
627
+ *
628
+ * @remarks
629
+ *
630
+ * The returned array has the same number of items, in the same order, as the
631
+ * given requests.
632
+ */
633
+ authorize(requests: AuthorizePermissionRequest[], options: PermissionsServiceRequestOptions): Promise<AuthorizePermissionResponse[]>;
634
+ /**
635
+ * Evaluates {@link @backstage/plugin-permission-common#ResourcePermission | ResourcePermissions} and returns both definitive and
636
+ * conditional decisions, depending on the configured
637
+ * {@link @backstage/plugin-permission-node#PermissionPolicy}.
638
+ *
639
+ * @remarks
640
+ *
641
+ * This method is useful when the
642
+ * caller needs more control over the processing of conditional decisions. For example, a plugin
643
+ * backend may want to use {@link @backstage/plugin-permission-common#PermissionCriteria | conditions} in a database query instead of
644
+ * evaluating each resource in memory.
645
+ *
646
+ * The returned array has the same number of items, in the same order, as the
647
+ * given requests.
648
+ */
649
+ authorizeConditional(requests: QueryPermissionRequest[], options: PermissionsServiceRequestOptions): Promise<QueryPermissionResponse[]>;
730
650
  }
731
651
 
732
652
  /**
733
- * HTTP route registration for root services.
653
+ * Prevent use of type parameter from contributing to type inference.
734
654
  *
735
- * See the {@link https://backstage.io/docs/backend-system/core-services/root-http-router | service documentation} for more details.
655
+ * https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-980401795
656
+ * @ignore
657
+ */
658
+ type NoInfer<T> = T extends infer S ? S : never;
659
+ /**
660
+ * Options for adding a resource type to the permission system.
736
661
  *
737
662
  * @public
738
663
  */
739
- interface RootHttpRouterService {
664
+ type PermissionsRegistryServiceAddResourceTypeOptions<TResourceType extends string, TResource> = {
740
665
  /**
741
- * Registers a handler at the root of the backend router.
742
- * The path is required and may not be empty.
666
+ * The identifier for the resource type.
743
667
  */
744
- use(path: string, handler: Handler): void;
668
+ resourceType: TResourceType;
669
+ /**
670
+ * Permissions that are available for this resource type.
671
+ */
672
+ permissions?: Array<Permission>;
673
+ /**
674
+ * Permission rules that are available for this resource type.
675
+ */
676
+ rules: PermissionRule<TResource, any, NoInfer<TResourceType>>[];
677
+ /**
678
+ * The function used to load associated resources based in the provided
679
+ * references.
680
+ *
681
+ * @remarks
682
+ *
683
+ * If this function is not provided the permission system will not be able to
684
+ * resolve conditional decisions except when requesting resources directly
685
+ * from the plugin.
686
+ */
687
+ getResources?(resourceRefs: string[]): Promise<Array<TResource | undefined>>;
688
+ };
689
+ /**
690
+ * Permission system integration for registering resources and permissions.
691
+ *
692
+ * See the {@link https://backstage.io/docs/permissions/overview | permissions documentation}
693
+ * and the {@link https://backstage.io/docs/backend-system/core-services/permission-integrations | service documentation}
694
+ * for more details.
695
+ *
696
+ * @public
697
+ */
698
+ interface PermissionsRegistryService {
699
+ /**
700
+ * Add permissions for this plugin to the permission system.
701
+ */
702
+ addPermissions(permissions: Permission[]): void;
703
+ /**
704
+ * Adds a set of permission rules to the permission system for a resource type
705
+ * that is owned by this plugin.
706
+ *
707
+ * @remarks
708
+ *
709
+ * Rules should be created using corresponding `create*PermissionRule`
710
+ * functions exported by plugins, who in turn are created with
711
+ * `makeCreatePermissionRule`.
712
+ *
713
+ * Rules can be added either directly by the plugin itself or through a plugin
714
+ * module.
715
+ */
716
+ addPermissionRules(rules: PermissionRule<any, any, string>[]): void;
717
+ /**
718
+ * Add a new resource type that is owned by this plugin to the permission
719
+ * system.
720
+ *
721
+ * @remarks
722
+ *
723
+ * To make this concrete, we can use the Backstage software catalog as an
724
+ * example. The catalog has conditional rules around access to specific
725
+ * _entities_ in the catalog. The _type_ of resource is captured here as
726
+ * `resourceType`, a string identifier (`catalog-entity` in this example) that
727
+ * can be provided with permission definitions. This is merely a _type_ to
728
+ * verify that conditions in an authorization policy are constructed
729
+ * correctly, not a reference to a specific resource.
730
+ *
731
+ * The `rules` parameter is an array of
732
+ * {@link @backstage/plugin-permission-node#PermissionRule}s that introduce
733
+ * conditional filtering logic for resources; for the catalog, these are
734
+ * things like `isEntityOwner` or `hasAnnotation`. Rules describe how to
735
+ * filter a list of resources, and the `conditions` returned allow these rules
736
+ * to be applied with specific parameters (such as 'group:default/team-a', or
737
+ * 'backstage.io/edit-url').
738
+ *
739
+ * The `getResources` argument should load resources based on a reference
740
+ * identifier. For the catalog, this is an
741
+ * [entity reference](https://backstage.io/docs/features/software-catalog/references#string-references).
742
+ * For other plugins, this can be any serialized format. This is used to add a
743
+ * permissions registry API via the HTTP router service. This API will be
744
+ * called by the `permission-backend` when authorization conditions relating
745
+ * to this plugin need to be evaluated.
746
+ */
747
+ addResourceType<const TResourceType extends string, TResource>(options: PermissionsRegistryServiceAddResourceTypeOptions<TResourceType, TResource>): void;
745
748
  }
746
749
 
747
750
  /**
@@ -759,563 +762,707 @@ interface PluginMetadataService {
759
762
  }
760
763
 
761
764
  /**
762
- * Represents a user principal (for example when a user Backstage token issued
763
- * by the auth backend was given to a request).
765
+ * Provides access to static configuration.
764
766
  *
765
- * @remarks
767
+ * See the {@link https://backstage.io/docs/conf/ | configuration documentation}
768
+ * and the {@link https://backstage.io/docs/backend-system/core-services/root-config | service documentation}
769
+ * for more details.
766
770
  *
767
- * Additional information about the user can be fetched using the
768
- * {@link UserInfoService}.
771
+ * @public
772
+ */
773
+ interface RootConfigService extends Config {
774
+ }
775
+
776
+ /**
777
+ * @public
778
+ */
779
+ interface RootHealthService {
780
+ /**
781
+ * Get the liveness status of the backend.
782
+ */
783
+ getLiveness(): Promise<{
784
+ status: number;
785
+ payload?: JsonValue;
786
+ }>;
787
+ /**
788
+ * Get the readiness status of the backend.
789
+ */
790
+ getReadiness(): Promise<{
791
+ status: number;
792
+ payload?: JsonValue;
793
+ }>;
794
+ }
795
+
796
+ /**
797
+ * HTTP route registration for root services.
798
+ *
799
+ * See the {@link https://backstage.io/docs/backend-system/core-services/root-http-router | service documentation} for more details.
769
800
  *
770
801
  * @public
771
802
  */
772
- type BackstageUserPrincipal = {
773
- type: 'user';
803
+ interface RootHttpRouterService {
774
804
  /**
775
- * The entity ref of the user entity that this principal represents.
805
+ * Registers a handler at the root of the backend router.
806
+ * The path is required and may not be empty.
776
807
  */
777
- userEntityRef: string;
778
- };
808
+ use(path: string, handler: Handler): void;
809
+ }
810
+
779
811
  /**
780
- * Represents a principal that is not authenticated (for example when no token
781
- * at all was given to a request).
812
+ * Registration of backend startup and shutdown lifecycle hooks.
813
+ *
814
+ * See the {@link https://backstage.io/docs/backend-system/core-services/root-lifecycle | service documentation} for more details.
782
815
  *
783
816
  * @public
784
817
  */
785
- type BackstageNonePrincipal = {
786
- type: 'none';
818
+ interface RootLifecycleService extends LifecycleService {
819
+ addBeforeShutdownHook(hook: () => void | Promise<void>): void;
820
+ }
821
+
822
+ /**
823
+ * Root-level logging.
824
+ *
825
+ * See the {@link https://backstage.io/docs/backend-system/core-services/root-logger | service documentation} for more details.
826
+ *
827
+ * @public
828
+ */
829
+ interface RootLoggerService extends LoggerService {
830
+ }
831
+
832
+ /**
833
+ * A function that can be called as a scheduled task.
834
+ *
835
+ * It may optionally accept an abort signal argument. When the signal triggers,
836
+ * processing should abort and return as quickly as possible.
837
+ *
838
+ * @public
839
+ */
840
+ type SchedulerServiceTaskFunction = ((abortSignal: AbortSignal) => void | Promise<void>) | (() => void | Promise<void>);
841
+ /**
842
+ * A semi-opaque type to describe an actively scheduled task.
843
+ *
844
+ * @public
845
+ */
846
+ type SchedulerServiceTaskDescriptor = {
847
+ /**
848
+ * The unique identifier of the task.
849
+ */
850
+ id: string;
851
+ /**
852
+ * The scope of the task.
853
+ */
854
+ scope: 'global' | 'local';
855
+ /**
856
+ * The settings that control the task flow. This is a semi-opaque structure
857
+ * that is mainly there for debugging purposes. Do not make any assumptions
858
+ * about the contents of this field.
859
+ */
860
+ settings: {
861
+ version: number;
862
+ } & JsonObject;
787
863
  };
788
864
  /**
789
- * Represents a service principal (for example when an external access method
790
- * token was given to a request, or the caller was a Backstage backend plugin).
865
+ * Options that control the scheduling of a task.
866
+ *
791
867
  * @public
792
868
  */
793
- type BackstageServicePrincipal = {
794
- type: 'service';
869
+ interface SchedulerServiceTaskScheduleDefinition {
795
870
  /**
796
- * A string that represents the service.
871
+ * How often you want the task to run. The system does its best to avoid
872
+ * overlapping invocations.
797
873
  *
798
874
  * @remarks
799
875
  *
800
- * This string is only informational, has no well defined semantics, and
801
- * should never be used to drive actual logic in code.
876
+ * This is the best effort value; under some circumstances there can be
877
+ * deviations. For example, if the task runtime is longer than the frequency
878
+ * and the timeout has not been given or not been exceeded yet, the next
879
+ * invocation of this task will be delayed until after the previous one
880
+ * finishes.
881
+ *
882
+ * This is a required field.
883
+ */
884
+ frequency: {
885
+ /**
886
+ * A crontab style string.
887
+ *
888
+ * @remarks
889
+ *
890
+ * Overview:
891
+ *
892
+ * ```
893
+ * ┌────────────── second (optional)
894
+ * │ ┌──────────── minute
895
+ * │ │ ┌────────── hour
896
+ * │ │ │ ┌──────── day of month
897
+ * │ │ │ │ ┌────── month
898
+ * │ │ │ │ │ ┌──── day of week
899
+ * │ │ │ │ │ │
900
+ * │ │ │ │ │ │
901
+ * * * * * * *
902
+ * ```
903
+ */
904
+ cron: string;
905
+ } | Duration | HumanDuration | {
906
+ trigger: 'manual';
907
+ };
908
+ /**
909
+ * The maximum amount of time that a single task invocation can take, before
910
+ * it's considered timed out and gets "released" such that a new invocation
911
+ * is permitted to take place (possibly, then, on a different worker).
802
912
  */
803
- subject: string;
913
+ timeout: Duration | HumanDuration;
804
914
  /**
805
- * The access restrictions that apply to this principal.
915
+ * The amount of time that should pass before the first invocation happens.
806
916
  *
807
917
  * @remarks
808
918
  *
809
- * If no access restrictions are provided the principal is assumed to have
810
- * unlimited access, at a framework level. The permissions system and
811
- * individual plugins may or may not still apply additional access controls on
812
- * top of this.
919
+ * This can be useful in cold start scenarios to stagger or delay some heavy
920
+ * compute jobs. If no value is given for this field then the first invocation
921
+ * will happen as soon as possible according to the cadence.
922
+ *
923
+ * NOTE: This is a per-worker delay. If you have a cluster of workers all
924
+ * collaborating on a task that has its `scope` field set to `'global'`, then
925
+ * you may still see the task being processed by other long-lived workers,
926
+ * while any given single worker is in its initial sleep delay time e.g. after
927
+ * a deployment. Therefore, this parameter is not useful for "globally" pausing
928
+ * work; its main intended use is for individual machines to get a chance to
929
+ * reach some equilibrium at startup before triggering heavy batch workloads.
813
930
  */
814
- accessRestrictions?: BackstagePrincipalAccessRestrictions;
815
- };
816
- /**
817
- * The access restrictions that apply to a given principal.
818
- *
819
- * @public
820
- */
821
- type BackstagePrincipalAccessRestrictions = {
931
+ initialDelay?: Duration | HumanDuration;
822
932
  /**
823
- * If given, the principal is limited to only performing actions with these
824
- * named permissions.
933
+ * Sets the scope of concurrency control / locking to apply for invocations of
934
+ * this task.
825
935
  *
826
- * Note that this only applies where permissions checks are enabled in the
827
- * first place. Endpoints that are not protected by the permissions system at
828
- * all, are not affected by this setting.
936
+ * @remarks
829
937
  *
830
- * This array always has at least one element, or is missing entirely.
831
- */
832
- permissionNames?: string[];
833
- /**
834
- * If given, the principal is limited to only performing actions whose
835
- * permissions have these attributes.
938
+ * When the scope is set to the default value `'global'`, the scheduler will
939
+ * attempt to ensure that only one worker machine runs the task at a time,
940
+ * according to the given cadence. This means that as the number of worker
941
+ * hosts increases, the invocation frequency of this task will not go up.
942
+ * Instead, the load is spread randomly across hosts. This setting is useful
943
+ * for tasks that access shared resources, for example catalog ingestion tasks
944
+ * where you do not want many machines to repeatedly import the same data and
945
+ * trample over each other.
836
946
  *
837
- * Note that this only applies where permissions checks are enabled in the
838
- * first place. Endpoints that are not protected by the permissions system at
839
- * all, are not affected by this setting.
947
+ * When the scope is set to `'local'`, there is no concurrency control across
948
+ * hosts. Each host runs the task according to the given cadence similarly to
949
+ * `setInterval`, but the runtime ensures that there are no overlapping runs.
840
950
  *
841
- * This object always has at least one key, or is missing entirely.
951
+ * @defaultValue 'global'
842
952
  */
843
- permissionAttributes?: {
844
- /**
845
- * Match any of these action values. This array always has at least one
846
- * element, or is missing entirely.
847
- */
848
- action?: Array<Required<PermissionAttributes>['action']>;
849
- };
850
- };
953
+ scope?: 'global' | 'local';
954
+ }
851
955
  /**
852
- * An opaque representation of credentials, for example as passed in a
853
- * request-response flow.
956
+ * Config options for {@link SchedulerServiceTaskScheduleDefinition}
957
+ * that control the scheduling of a task.
854
958
  *
855
959
  * @public
856
960
  */
857
- type BackstageCredentials<TPrincipal = unknown> = {
858
- $$type: '@backstage/BackstageCredentials';
859
- /**
860
- * If the credentials have a limited lifetime, this is the time at which they
861
- * expire and may no longer be accepted by a receiver.
862
- */
863
- expiresAt?: Date;
961
+ interface SchedulerServiceTaskScheduleDefinitionConfig {
864
962
  /**
865
- * The principal (originator) of the request.
963
+ * How often you want the task to run. The system does its best to avoid
964
+ * overlapping invocations.
866
965
  *
867
966
  * @remarks
868
967
  *
869
- * This is semantically the originator of a request chain, and may or may not
870
- * represent the immediate caller of your service. For example, in
871
- * on-behalf-of scenarios, the immediate caller may be an intermediary backend
872
- * service, but the principal may still be a user that was the original
873
- * caller.
874
- */
875
- principal: TPrincipal;
876
- };
877
- /**
878
- * The types of principal that can be represented in a
879
- * {@link BackstageCredentials} object.
880
- *
881
- * @public
882
- */
883
- type BackstagePrincipalTypes = {
884
- user: BackstageUserPrincipal;
885
- service: BackstageServicePrincipal;
886
- none: BackstageNonePrincipal;
887
- unknown: unknown;
888
- };
889
- /**
890
- * Provides token authentication and credentials management.
891
- *
892
- * See the {@link https://backstage.io/docs/backend-system/core-services/auth | service documentation} for more details.
893
- *
894
- * @public
895
- */
896
- interface AuthService {
897
- /**
898
- * Verifies a token and returns the associated credentials.
968
+ * This is the best effort value; under some circumstances there can be
969
+ * deviations. For example, if the task runtime is longer than the frequency
970
+ * and the timeout has not been given or not been exceeded yet, the next
971
+ * invocation of this task will be delayed until after the previous one
972
+ * finishes.
973
+ *
974
+ * This is a required field.
899
975
  */
900
- authenticate(token: string, options?: {
976
+ frequency: {
901
977
  /**
902
- * If set to true, allow limited access tokens (such as cookies).
978
+ * A crontab style string.
903
979
  *
904
- * If this flag is not set, or is set to false, calls with limited access
905
- * tokens will lead to a {@link @backstage/errors#NotAllowedError} being
906
- * thrown.
980
+ * @remarks
981
+ *
982
+ * Overview:
983
+ *
984
+ * ```
985
+ * ┌────────────── second (optional)
986
+ * │ ┌──────────── minute
987
+ * │ │ ┌────────── hour
988
+ * │ │ │ ┌──────── day of month
989
+ * │ │ │ │ ┌────── month
990
+ * │ │ │ │ │ ┌──── day of week
991
+ * │ │ │ │ │ │
992
+ * │ │ │ │ │ │
993
+ * * * * * * *
994
+ * ```
907
995
  */
908
- allowLimitedAccess?: boolean;
909
- }): Promise<BackstageCredentials>;
996
+ cron: string;
997
+ } | string | HumanDuration
910
998
  /**
911
- * Checks if the given credentials are of the given type, and narrows the
912
- * TypeScript type accordingly if there's a match.
999
+ * This task will only run when manually triggered with the `triggerTask` method; no automatic
1000
+ * scheduling. This is useful for locking of global tasks that should not be run concurrently.
913
1001
  */
914
- isPrincipal<TType extends keyof BackstagePrincipalTypes>(credentials: BackstageCredentials, type: TType): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]>;
1002
+ | {
1003
+ trigger: 'manual';
1004
+ };
915
1005
  /**
916
- * Create a credentials object that represents an unauthenticated caller.
1006
+ * The maximum amount of time that a single task invocation can take, before
1007
+ * it's considered timed out and gets "released" such that a new invocation
1008
+ * is permitted to take place (possibly, then, on a different worker).
917
1009
  */
918
- getNoneCredentials(): Promise<BackstageCredentials<BackstageNonePrincipal>>;
1010
+ timeout: string | HumanDuration;
919
1011
  /**
920
- * Create a credentials object that represents the current service itself.
1012
+ * The amount of time that should pass before the first invocation happens.
1013
+ *
1014
+ * @remarks
1015
+ *
1016
+ * This can be useful in cold start scenarios to stagger or delay some heavy
1017
+ * compute jobs. If no value is given for this field then the first invocation
1018
+ * will happen as soon as possible according to the cadence.
1019
+ *
1020
+ * NOTE: This is a per-worker delay. If you have a cluster of workers all
1021
+ * collaborating on a task that has its `scope` field set to `'global'`, then
1022
+ * you may still see the task being processed by other long-lived workers,
1023
+ * while any given single worker is in its initial sleep delay time e.g. after
1024
+ * a deployment. Therefore, this parameter is not useful for "globally" pausing
1025
+ * work; its main intended use is for individual machines to get a chance to
1026
+ * reach some equilibrium at startup before triggering heavy batch workloads.
921
1027
  */
922
- getOwnServiceCredentials(): Promise<BackstageCredentials<BackstageServicePrincipal>>;
1028
+ initialDelay?: string | HumanDuration;
923
1029
  /**
924
- * Issue a token that can be used for authenticating calls towards other
925
- * backend plugins.
1030
+ * Sets the scope of concurrency control / locking to apply for invocations of
1031
+ * this task.
926
1032
  *
927
1033
  * @remarks
928
1034
  *
929
- * This method should be called before each request. Do not hold on to the
930
- * issued token and reuse it for future calls.
1035
+ * When the scope is set to the default value `'global'`, the scheduler will
1036
+ * attempt to ensure that only one worker machine runs the task at a time,
1037
+ * according to the given cadence. This means that as the number of worker
1038
+ * hosts increases, the invocation frequency of this task will not go up.
1039
+ * Instead, the load is spread randomly across hosts. This setting is useful
1040
+ * for tasks that access shared resources, for example catalog ingestion tasks
1041
+ * where you do not want many machines to repeatedly import the same data and
1042
+ * trample over each other.
1043
+ *
1044
+ * When the scope is set to `'local'`, there is no concurrency control across
1045
+ * hosts. Each host runs the task according to the given cadence similarly to
1046
+ * `setInterval`, but the runtime ensures that there are no overlapping runs.
1047
+ *
1048
+ * @defaultValue 'global'
931
1049
  */
932
- getPluginRequestToken(options: {
933
- /**
934
- * The credentials of the originator of the request.
935
- *
936
- * @remarks
937
- *
938
- * This is most commonly the result of
939
- * {@link AuthService.getOwnServiceCredentials} when the current service is
940
- * the originator, or the output of {@link HttpAuthService.credentials} when
941
- * performing requests on behalf of an incoming request identity.
942
- */
943
- onBehalfOf: BackstageCredentials;
944
- /**
945
- * The ID of the plugin that the request is being made to.
946
- */
947
- targetPluginId: string;
948
- }): Promise<{
949
- token: string;
950
- }>;
1050
+ scope?: 'global' | 'local';
1051
+ }
1052
+ /**
1053
+ * Options that apply to the invocation of a given task.
1054
+ *
1055
+ * @public
1056
+ */
1057
+ interface SchedulerServiceTaskInvocationDefinition {
951
1058
  /**
952
- * Issue a limited user token that can be used e.g. in cookie flows.
1059
+ * A unique ID (within the scope of the plugin) for the task.
953
1060
  */
954
- getLimitedUserToken(
1061
+ id: string;
955
1062
  /**
956
- * The credentials that this token should represent. Must be a user
957
- * principal. Commonly the output of {@link HttpAuthService.credentials} is
958
- * used as the input.
1063
+ * The actual task function to be invoked regularly.
959
1064
  */
960
- credentials: BackstageCredentials<BackstageUserPrincipal>): Promise<{
961
- token: string;
962
- expiresAt: Date;
963
- }>;
1065
+ fn: SchedulerServiceTaskFunction;
964
1066
  /**
965
- * Retrieve the public keys that have been used to sign tokens that were
966
- * issued by this service. This list is periodically pruned from keys that are
967
- * significantly past their expiry.
1067
+ * An abort signal that, when triggered, will stop the recurring execution of
1068
+ * the task.
968
1069
  */
969
- listPublicServiceKeys(): Promise<{
970
- keys: JsonObject[];
971
- }>;
1070
+ signal?: AbortSignal;
972
1071
  }
973
-
974
1072
  /**
975
- * Options for {@link PermissionsService} requests.
1073
+ * A previously prepared task schedule, ready to be invoked.
976
1074
  *
977
1075
  * @public
978
1076
  */
979
- interface PermissionsServiceRequestOptions extends EvaluatorRequestOptions {
980
- credentials: BackstageCredentials;
1077
+ interface SchedulerServiceTaskRunner {
1078
+ /**
1079
+ * Takes the schedule and executes an actual task using it.
1080
+ *
1081
+ * @param task - The actual runtime properties of the task
1082
+ */
1083
+ run(task: SchedulerServiceTaskInvocationDefinition): Promise<void>;
981
1084
  }
982
1085
  /**
983
- * Permission system integration for authorization of user/service actions.
1086
+ * Deals with the scheduling of distributed tasks, for a given plugin.
984
1087
  *
985
- * See the {@link https://backstage.io/docs/permissions/overview | permissions documentation}
986
- * and the {@link https://backstage.io/docs/backend-system/core-services/permissions | service documentation}
987
- * for more details.
1088
+ * See the {@link https://backstage.io/docs/backend-system/core-services/scheduler | service documentation} for more details.
988
1089
  *
989
1090
  * @public
990
1091
  */
991
- interface PermissionsService extends PermissionEvaluator {
1092
+ interface SchedulerService {
992
1093
  /**
993
- * Evaluates
994
- * {@link @backstage/plugin-permission-common#Permission | Permissions} and
995
- * returns definitive decisions.
1094
+ * Manually triggers a task by ID.
1095
+ *
1096
+ * If the task doesn't exist, a NotFoundError is thrown. If the task is
1097
+ * currently running, a ConflictError is thrown.
1098
+ *
1099
+ * @param id - The task ID
1100
+ */
1101
+ triggerTask(id: string): Promise<void>;
1102
+ /**
1103
+ * Schedules a task function for recurring runs.
996
1104
  *
997
1105
  * @remarks
998
1106
  *
999
- * The returned array has the same number of items, in the same order, as the
1000
- * given requests.
1107
+ * The `scope` task field controls whether to use coordinated exclusive
1108
+ * invocation across workers, or to just coordinate within the current worker.
1109
+ *
1110
+ * This convenience method performs both the scheduling and invocation in one
1111
+ * go.
1112
+ *
1113
+ * @param task - The task definition
1001
1114
  */
1002
- authorize(requests: AuthorizePermissionRequest[], options: PermissionsServiceRequestOptions): Promise<AuthorizePermissionResponse[]>;
1115
+ scheduleTask(task: SchedulerServiceTaskScheduleDefinition & SchedulerServiceTaskInvocationDefinition): Promise<void>;
1003
1116
  /**
1004
- * Evaluates {@link @backstage/plugin-permission-common#ResourcePermission | ResourcePermissions} and returns both definitive and
1005
- * conditional decisions, depending on the configured
1006
- * {@link @backstage/plugin-permission-node#PermissionPolicy}.
1117
+ * Creates a scheduled but dormant recurring task, ready to be launched at a
1118
+ * later time.
1007
1119
  *
1008
1120
  * @remarks
1009
1121
  *
1010
- * This method is useful when the
1011
- * caller needs more control over the processing of conditional decisions. For example, a plugin
1012
- * backend may want to use {@link @backstage/plugin-permission-common#PermissionCriteria | conditions} in a database query instead of
1013
- * evaluating each resource in memory.
1122
+ * This method is useful for pre-creating a schedule in outer code to be
1123
+ * passed into an inner implementation, such that the outer code controls
1124
+ * scheduling while inner code controls implementation.
1014
1125
  *
1015
- * The returned array has the same number of items, in the same order, as the
1016
- * given requests.
1126
+ * @param schedule - The task schedule
1017
1127
  */
1018
- authorizeConditional(requests: QueryPermissionRequest[], options: PermissionsServiceRequestOptions): Promise<QueryPermissionResponse[]>;
1128
+ createScheduledTaskRunner(schedule: SchedulerServiceTaskScheduleDefinition): SchedulerServiceTaskRunner;
1129
+ /**
1130
+ * Returns all scheduled tasks registered to this scheduler.
1131
+ *
1132
+ * @remarks
1133
+ *
1134
+ * This method is useful for triggering tasks manually using the triggerTask
1135
+ * functionality. Note that the returned tasks contain only tasks that have
1136
+ * been initialized in this instance of the scheduler.
1137
+ *
1138
+ * @returns Scheduled tasks
1139
+ */
1140
+ getScheduledTasks(): Promise<SchedulerServiceTaskDescriptor[]>;
1019
1141
  }
1020
-
1021
1142
  /**
1022
- * Options for {@link HttpRouterService.addAuthPolicy}.
1143
+ * Reads a {@link SchedulerServiceTaskScheduleDefinition} from config. Expects
1144
+ * the config not to be the root config, but the config for the definition.
1023
1145
  *
1146
+ * @param config - config for a TaskScheduleDefinition.
1024
1147
  * @public
1025
1148
  */
1026
- interface HttpRouterServiceAuthPolicy {
1027
- path: string;
1028
- allow: 'unauthenticated' | 'user-cookie';
1029
- }
1149
+ declare function readSchedulerServiceTaskScheduleDefinitionFromConfig(config: Config): SchedulerServiceTaskScheduleDefinition;
1150
+
1030
1151
  /**
1031
- * Allows plugins to register HTTP routes.
1152
+ * A generic interface for fetching plain data from URLs.
1032
1153
  *
1033
- * See the {@link https://backstage.io/docs/backend-system/core-services/http-router | service documentation} for more details.
1154
+ * See the {@link https://backstage.io/docs/backend-system/core-services/url-reader | service documentation} for more details.
1034
1155
  *
1035
1156
  * @public
1036
1157
  */
1037
- interface HttpRouterService {
1158
+ interface UrlReaderService {
1038
1159
  /**
1039
- * Registers an Express request handler under the plugin's base router. This
1040
- * typically makes its base path `/api/<plugin-id>`.
1160
+ * Reads a single file and return its content.
1041
1161
  */
1042
- use(handler: Handler): void;
1162
+ readUrl(url: string, options?: UrlReaderServiceReadUrlOptions): Promise<UrlReaderServiceReadUrlResponse>;
1043
1163
  /**
1044
- * Adds an auth policy to the router. This is used to allow unauthenticated or
1045
- * cookie based access to parts of a plugin's API.
1046
- *
1047
- * @remarks
1048
- *
1049
- * The paths given follow the same pattern as the routers given to the `use`
1050
- * method, that is, they are relative to the plugin's base URL, and can
1051
- * contain placeholders.
1052
- *
1053
- * @example
1054
- *
1055
- * ```ts
1056
- * http.addAuthPolicy({
1057
- * path: '/static/:id',
1058
- * allow: 'user-cookie',
1059
- * });
1060
- * ```
1061
- *
1062
- * This allows limited access tokens via cookies on the
1063
- * `/api/<plugin-id>/static/*` paths, but not unauthenticated access.
1164
+ * Reads a full or partial file tree.
1064
1165
  */
1065
- addAuthPolicy(policy: HttpRouterServiceAuthPolicy): void;
1166
+ readTree(url: string, options?: UrlReaderServiceReadTreeOptions): Promise<UrlReaderServiceReadTreeResponse>;
1167
+ /**
1168
+ * Searches for a file in a tree using a glob pattern.
1169
+ */
1170
+ search(url: string, options?: UrlReaderServiceSearchOptions): Promise<UrlReaderServiceSearchResponse>;
1066
1171
  }
1067
-
1068
1172
  /**
1069
- * Provides handling of credentials in an ongoing request.
1070
- *
1071
- * See the {@link https://backstage.io/docs/backend-system/core-services/http-auth | service documentation} for more details.
1173
+ * An options object for readUrl operations.
1072
1174
  *
1073
1175
  * @public
1074
1176
  */
1075
- interface HttpAuthService {
1177
+ type UrlReaderServiceReadUrlOptions = {
1076
1178
  /**
1077
- * Extracts the caller's credentials from a request.
1179
+ * An ETag which can be provided to check whether a
1180
+ * {@link UrlReaderService.readUrl} response has changed from a previous execution.
1078
1181
  *
1079
1182
  * @remarks
1080
1183
  *
1081
- * The credentials have been validated before returning, and are guaranteed to
1082
- * adhere to whatever policies have been added to this route using
1083
- * {@link HttpRouterService.addAuthPolicy}, if any.
1084
- *
1085
- * Further restrictions can be imposed by passing in options that control the
1086
- * allowed types of credential.
1184
+ * In the {@link UrlReaderService.readUrl} response, an ETag is returned along with
1185
+ * the data. The ETag is a unique identifier of the data, usually the commit
1186
+ * SHA or ETag from the target.
1087
1187
  *
1088
- * You can narrow the returned credentials object to specific principal types
1089
- * using {@link AuthService.isPrincipal}.
1188
+ * When an ETag is given in ReadUrlOptions, {@link UrlReaderService.readUrl} will
1189
+ * first compare the ETag against the ETag of the target. If they match,
1190
+ * {@link UrlReaderService.readUrl} will throw a
1191
+ * {@link @backstage/errors#NotModifiedError} indicating that the response
1192
+ * will not differ from the previous response which included this particular
1193
+ * ETag. If they do not match, {@link UrlReaderService.readUrl} will return the rest
1194
+ * of the response along with a new ETag.
1090
1195
  */
1091
- credentials<TAllowed extends keyof BackstagePrincipalTypes = 'unknown'>(
1196
+ etag?: string;
1092
1197
  /**
1093
- * An Express request object.
1198
+ * A date which can be provided to check whether a
1199
+ * {@link UrlReaderService.readUrl} response has changed since the lastModifiedAt.
1200
+ *
1201
+ * @remarks
1202
+ *
1203
+ * In the {@link UrlReaderService.readUrl} response, an lastModifiedAt is returned
1204
+ * along with data. The lastModifiedAt date represents the last time the data
1205
+ * was modified.
1206
+ *
1207
+ * When an lastModifiedAfter is given in ReadUrlOptions, {@link UrlReaderService.readUrl}
1208
+ * will compare the lastModifiedAfter against the lastModifiedAt of the target. If
1209
+ * the data has not been modified since this date, the {@link UrlReaderService.readUrl}
1210
+ * will throw a {@link @backstage/errors#NotModifiedError} indicating that the
1211
+ * response does not contain any new data. If they do not match,
1212
+ * {@link UrlReaderService.readUrl} will return the rest of the response along with new
1213
+ * lastModifiedAt date.
1094
1214
  */
1095
- req: Request<any, any, any, any, any>,
1215
+ lastModifiedAfter?: Date;
1096
1216
  /**
1097
- * Optional further restrictions.
1217
+ * An abort signal to pass down to the underlying request.
1218
+ *
1219
+ * @remarks
1220
+ *
1221
+ * Not all reader implementations may take this field into account.
1098
1222
  */
1099
- options?: {
1100
- /**
1101
- * If specified, allow only principals of the given type(s).
1102
- *
1103
- * If the incoming credentials were not of a type that matched this
1104
- * restriction, a {@link @backstage/errors#NotAllowedError} is thrown.
1105
- *
1106
- * The default is to allow user and service principals.
1107
- */
1108
- allow?: Array<TAllowed>;
1109
- /**
1110
- * If set to true, allow limited access tokens (such as cookies).
1111
- *
1112
- * If this flag is not set, or is set to false, calls with limited access
1113
- * tokens will lead to a {@link @backstage/errors#NotAllowedError} being
1114
- * thrown.
1115
- */
1116
- allowLimitedAccess?: boolean;
1117
- }): Promise<BackstageCredentials<BackstagePrincipalTypes[TAllowed]>>;
1223
+ signal?: AbortSignal;
1118
1224
  /**
1119
- * Issues a limited access token as a cookie on the given response object.
1120
- * This is only possible for requests that were originally made with user
1121
- * credentials (such as a Backstage token).
1225
+ * An optional token to use for authentication when reading the resources.
1122
1226
  *
1123
- * This must be called before sending any payload data.
1227
+ * @remarks
1228
+ *
1229
+ * By default all URL Readers will use the integrations config which is supplied
1230
+ * when creating the Readers. Sometimes it might be desireable to use the already
1231
+ * created URLReaders but with a different token, maybe that's supplied by the user
1232
+ * at runtime.
1124
1233
  */
1125
- issueUserCookie(
1234
+ token?: string;
1235
+ };
1236
+ /**
1237
+ * A response object for {@link UrlReaderService.readUrl} operations.
1238
+ *
1239
+ * @public
1240
+ */
1241
+ type UrlReaderServiceReadUrlResponse = {
1126
1242
  /**
1127
- * An Express response object.
1243
+ * Returns the data that was read from the remote URL.
1128
1244
  */
1129
- res: Response,
1245
+ buffer(): Promise<Buffer>;
1130
1246
  /**
1131
- * Optional further settings.
1247
+ * Returns the data that was read from the remote URL as a Readable stream.
1248
+ *
1249
+ * @remarks
1250
+ *
1251
+ * This method will be required in a future release.
1132
1252
  */
1133
- options?: {
1134
- /**
1135
- * Issue the cookie for this specific credential. Must be a "user" type
1136
- * principal, or a "none" type (which leads to deleting the cookie).
1137
- *
1138
- * @remarks
1139
- *
1140
- * Normally you do not have to specify this option, because the default
1141
- * behavior is to extract the credentials from the request that
1142
- * corresponded to the given respnse.
1143
- */
1144
- credentials?: BackstageCredentials;
1145
- }): Promise<{
1146
- expiresAt: Date;
1147
- }>;
1148
- }
1149
-
1150
- /**
1151
- * @public
1152
- */
1153
- interface RootHealthService {
1253
+ stream?(): Readable;
1154
1254
  /**
1155
- * Get the liveness status of the backend.
1255
+ * Etag returned by content provider.
1256
+ *
1257
+ * @remarks
1258
+ *
1259
+ * Can be used to compare and cache responses when doing subsequent calls.
1156
1260
  */
1157
- getLiveness(): Promise<{
1158
- status: number;
1159
- payload?: JsonValue;
1160
- }>;
1261
+ etag?: string;
1161
1262
  /**
1162
- * Get the readiness status of the backend.
1263
+ * Last modified date of the file contents.
1163
1264
  */
1164
- getReadiness(): Promise<{
1165
- status: number;
1166
- payload?: JsonValue;
1167
- }>;
1168
- }
1169
-
1265
+ lastModifiedAt?: Date;
1266
+ };
1170
1267
  /**
1171
- * The DiscoveryService is used to provide a mechanism for backend
1172
- * plugins to discover the endpoints for itself or other backend plugins.
1173
- *
1174
- * See the {@link https://backstage.io/docs/backend-system/core-services/discovery | service documentation} for more details.
1175
- *
1176
- * @remarks
1177
- *
1178
- * The purpose of the discovery API is to allow for many different deployment
1179
- * setups and routing methods through a central configuration, instead
1180
- * of letting each individual plugin manage that configuration.
1181
- *
1182
- * Implementations of the discovery API can be as simple as a URL pattern
1183
- * using the pluginId, but could also have overrides for individual plugins,
1184
- * or query a separate discovery service.
1268
+ * An options object for {@link UrlReaderService.readTree} operations.
1185
1269
  *
1186
1270
  * @public
1187
1271
  */
1188
- interface DiscoveryService {
1272
+ type UrlReaderServiceReadTreeOptions = {
1189
1273
  /**
1190
- * Returns the internal HTTP base URL for a given plugin, without a trailing slash.
1274
+ * A filter that can be used to select which files should be included.
1191
1275
  *
1192
1276
  * @remarks
1193
1277
  *
1194
- * The returned URL should point to an internal endpoint for the plugin, with
1195
- * the shortest route possible. The URL should be used for service-to-service
1196
- * communication within a Backstage backend deployment.
1278
+ * The path passed to the filter function is the relative path from the URL
1279
+ * that the file tree is fetched from, without any leading '/'.
1197
1280
  *
1198
- * This method must always be called just before making each request, as opposed to
1199
- * fetching the URL once when constructing an API client. That is to ensure that more
1200
- * flexible routing patterns can be supported where a different result might be returned each time.
1281
+ * For example, given the URL https://github.com/my/repo/tree/master/my-dir, a file
1282
+ * at https://github.com/my/repo/blob/master/my-dir/my-subdir/my-file.txt will
1283
+ * be represented as my-subdir/my-file.txt
1201
1284
  *
1202
- * For example, asking for the URL for `catalog` may return something
1203
- * like `http://10.1.2.3/api/catalog`
1285
+ * If no filter is provided, all files are extracted.
1204
1286
  */
1205
- getBaseUrl(pluginId: string): Promise<string>;
1287
+ filter?(path: string, info?: {
1288
+ size: number;
1289
+ }): boolean;
1206
1290
  /**
1207
- * Returns the external HTTP base backend URL for a given plugin, without a trailing slash.
1291
+ * An ETag which can be provided to check whether a
1292
+ * {@link UrlReaderService.readTree} response has changed from a previous execution.
1208
1293
  *
1209
1294
  * @remarks
1210
1295
  *
1211
- * The returned URL should point to an external endpoint for the plugin, such that
1212
- * it is reachable from the Backstage frontend and other external services. The returned
1213
- * URL should be usable for example as a callback / webhook URL.
1296
+ * In the {@link UrlReaderService.readTree} response, an ETag is returned along with
1297
+ * the tree blob. The ETag is a unique identifier of the tree blob, usually
1298
+ * the commit SHA or ETag from the target.
1214
1299
  *
1215
- * The returned URL should be stable and in general not change unless other static
1216
- * or external configuration is changed. Changes should not come as a surprise
1217
- * to an operator of the Backstage backend.
1300
+ * When an ETag is given as a request option, {@link UrlReaderService.readTree} will
1301
+ * first compare the ETag against the ETag on the target branch. If they
1302
+ * match, {@link UrlReaderService.readTree} will throw a
1303
+ * {@link @backstage/errors#NotModifiedError} indicating that the response
1304
+ * will not differ from the previous response which included this particular
1305
+ * ETag. If they do not match, {@link UrlReaderService.readTree} will return the
1306
+ * rest of the response along with a new ETag.
1307
+ */
1308
+ etag?: string;
1309
+ /**
1310
+ * An abort signal to pass down to the underlying request.
1218
1311
  *
1219
- * For example, asking for the URL for `catalog` may return something
1220
- * like `https://backstage.example.com/api/catalog`
1312
+ * @remarks
1313
+ *
1314
+ * Not all reader implementations may take this field into account.
1221
1315
  */
1222
- getExternalBaseUrl(pluginId: string): Promise<string>;
1223
- }
1224
-
1316
+ signal?: AbortSignal;
1317
+ /**
1318
+ * An optional token to use for authentication when reading the resources.
1319
+ *
1320
+ * @remarks
1321
+ *
1322
+ * By default all URL Readers will use the integrations config which is supplied
1323
+ * when creating the Readers. Sometimes it might be desireable to use the already
1324
+ * created URLReaders but with a different token, maybe that's supplied by the user
1325
+ * at runtime.
1326
+ */
1327
+ token?: string;
1328
+ };
1225
1329
  /**
1226
- * Manages access to databases that plugins get.
1330
+ * Options that control {@link UrlReaderServiceReadTreeResponse.dir} execution.
1227
1331
  *
1228
- * See the {@link https://backstage.io/docs/backend-system/core-services/database | service documentation} for more details.
1332
+ * @public
1333
+ */
1334
+ type UrlReaderServiceReadTreeResponseDirOptions = {
1335
+ /**
1336
+ * The directory to write files to.
1337
+ *
1338
+ * @remarks
1339
+ *
1340
+ * Defaults to the OS tmpdir, or `backend.workingDirectory` if set in config.
1341
+ */
1342
+ targetDir?: string;
1343
+ };
1344
+ /**
1345
+ * A response object for {@link UrlReaderService.readTree} operations.
1229
1346
  *
1230
1347
  * @public
1231
1348
  */
1232
- interface DatabaseService {
1349
+ type UrlReaderServiceReadTreeResponse = {
1233
1350
  /**
1234
- * getClient provides backend plugins database connections for itself.
1351
+ * Returns an array of all the files inside the tree, and corresponding
1352
+ * functions to read their content.
1353
+ */
1354
+ files(): Promise<UrlReaderServiceReadTreeResponseFile[]>;
1355
+ /**
1356
+ * Returns the tree contents as a binary archive, using a stream.
1357
+ */
1358
+ archive(): Promise<NodeJS.ReadableStream>;
1359
+ /**
1360
+ * Extracts the tree response into a directory and returns the path of the
1361
+ * directory.
1235
1362
  *
1236
- * The purpose of this method is to allow plugins to get isolated data
1237
- * stores so that plugins are discouraged from database integration.
1363
+ * **NOTE**: It is the responsibility of the caller to remove the directory after use.
1238
1364
  */
1239
- getClient(): Promise<Knex>;
1365
+ dir(options?: UrlReaderServiceReadTreeResponseDirOptions): Promise<string>;
1240
1366
  /**
1241
- * This property is used to control the behavior of database migrations.
1367
+ * Etag returned by content provider.
1368
+ *
1369
+ * @remarks
1370
+ *
1371
+ * Can be used to compare and cache responses when doing subsequent calls.
1242
1372
  */
1243
- migrations?: {
1244
- /**
1245
- * skip database migrations. Useful if connecting to a read-only database.
1246
- *
1247
- * @defaultValue false
1248
- */
1249
- skip?: boolean;
1250
- };
1251
- }
1252
-
1373
+ etag: string;
1374
+ };
1253
1375
  /**
1254
- * Provides access to static configuration.
1255
- *
1256
- * See the {@link https://backstage.io/docs/conf/ | configuration documentation}
1257
- * and the {@link https://backstage.io/docs/backend-system/core-services/root-config | service documentation}
1258
- * for more details.
1376
+ * Represents a single file in a {@link UrlReaderService.readTree} response.
1259
1377
  *
1260
1378
  * @public
1261
1379
  */
1262
- interface RootConfigService extends Config {
1263
- }
1264
-
1380
+ type UrlReaderServiceReadTreeResponseFile = {
1381
+ /**
1382
+ * The filepath of the data.
1383
+ */
1384
+ path: string;
1385
+ /**
1386
+ * The binary contents of the file.
1387
+ */
1388
+ content(): Promise<Buffer>;
1389
+ /**
1390
+ * The last modified timestamp of the data.
1391
+ */
1392
+ lastModifiedAt?: Date;
1393
+ };
1265
1394
  /**
1266
- * Options passed to {@link CacheService.set}.
1395
+ * An options object for search operations.
1267
1396
  *
1268
1397
  * @public
1269
1398
  */
1270
- type CacheServiceSetOptions = {
1399
+ type UrlReaderServiceSearchOptions = {
1271
1400
  /**
1272
- * Optional TTL (in milliseconds if given as a number). Defaults to the TTL provided when the client
1273
- * was set up (or no TTL if none are provided).
1401
+ * An etag can be provided to check whether the search response has changed from a previous execution.
1402
+ *
1403
+ * In the search() response, an etag is returned along with the files. The etag is a unique identifier
1404
+ * of the current tree, usually the commit SHA or etag from the target.
1405
+ *
1406
+ * When an etag is given in SearchOptions, search will first compare the etag against the etag
1407
+ * on the target branch. If they match, search will throw a NotModifiedError indicating that the search
1408
+ * response will not differ from the previous response which included this particular etag. If they mismatch,
1409
+ * search will return the rest of SearchResponse along with a new etag.
1274
1410
  */
1275
- ttl?: number | HumanDuration;
1411
+ etag?: string;
1412
+ /**
1413
+ * An abort signal to pass down to the underlying request.
1414
+ *
1415
+ * @remarks
1416
+ *
1417
+ * Not all reader implementations may take this field into account.
1418
+ */
1419
+ signal?: AbortSignal;
1420
+ /**
1421
+ * An optional token to use for authentication when reading the resources.
1422
+ *
1423
+ * @remarks
1424
+ *
1425
+ * By default all URL Readers will use the integrations config which is supplied
1426
+ * when creating the Readers. Sometimes it might be desireable to use the already
1427
+ * created URLReaders but with a different token, maybe that's supplied by the user
1428
+ * at runtime.
1429
+ */
1430
+ token?: string;
1276
1431
  };
1277
1432
  /**
1278
- * Options passed to {@link CacheService.withOptions}.
1433
+ * The output of a search operation.
1279
1434
  *
1280
1435
  * @public
1281
1436
  */
1282
- type CacheServiceOptions = {
1437
+ type UrlReaderServiceSearchResponse = {
1283
1438
  /**
1284
- * An optional default TTL (in milliseconds if given as a number) to be set when getting a client
1285
- * instance. If not provided, data will persist indefinitely by default (or
1286
- * can be configured per entry at set-time).
1439
+ * The files that matched the search query.
1287
1440
  */
1288
- defaultTtl?: number | HumanDuration;
1441
+ files: UrlReaderServiceSearchResponseFile[];
1442
+ /**
1443
+ * A unique identifier of the current remote tree, usually the commit SHA or etag from the target.
1444
+ */
1445
+ etag: string;
1289
1446
  };
1290
1447
  /**
1291
- * A pre-configured, storage agnostic cache service suitable for use by
1292
- * Backstage plugins.
1293
- *
1294
- * See the {@link https://backstage.io/docs/backend-system/core-services/cache | service documentation} for more details.
1448
+ * Represents a single file in a search response.
1295
1449
  *
1296
1450
  * @public
1297
1451
  */
1298
- interface CacheService {
1299
- /**
1300
- * Reads data from a cache store for the given key. If no data was found,
1301
- * returns undefined.
1302
- */
1303
- get<TValue extends JsonValue>(key: string): Promise<TValue | undefined>;
1452
+ type UrlReaderServiceSearchResponseFile = {
1304
1453
  /**
1305
- * Writes the given data to a cache store, associated with the given key. An
1306
- * optional TTL may also be provided, otherwise it defaults to the TTL that
1307
- * was provided when the client was instantiated.
1454
+ * The full URL to the file.
1308
1455
  */
1309
- set(key: string, value: JsonValue, options?: CacheServiceSetOptions): Promise<void>;
1456
+ url: string;
1310
1457
  /**
1311
- * Removes the given key from the cache store.
1458
+ * The binary contents of the file.
1312
1459
  */
1313
- delete(key: string): Promise<void>;
1460
+ content(): Promise<Buffer>;
1314
1461
  /**
1315
- * Creates a new {@link CacheService} instance with the given options.
1462
+ * The last modified timestamp of the data.
1316
1463
  */
1317
- withOptions(options: CacheServiceOptions): CacheService;
1318
- }
1464
+ lastModifiedAt?: Date;
1465
+ };
1319
1466
 
1320
1467
  /**
1321
1468
  * Represents user information that is available to the backend, based on some
@@ -1587,6 +1734,16 @@ declare namespace coreServices {
1587
1734
  * @public
1588
1735
  */
1589
1736
  const logger: ServiceRef<LoggerService, "plugin", "singleton">;
1737
+ /**
1738
+ * Plugin-level auditing.
1739
+ *
1740
+ * See {@link AuditorService}
1741
+ * and {@link https://backstage.io/docs/backend-system/core-services/auditor | the service docs}
1742
+ * for more information.
1743
+ *
1744
+ * @public
1745
+ */
1746
+ const auditor: ServiceRef<AuditorService, "plugin", "singleton">;
1590
1747
  /**
1591
1748
  * Permission system integration for authorization of user actions.
1592
1749
  *
@@ -1597,6 +1754,16 @@ declare namespace coreServices {
1597
1754
  * @public
1598
1755
  */
1599
1756
  const permissions: ServiceRef<PermissionsService, "plugin", "singleton">;
1757
+ /**
1758
+ * Permission system integration for registering resources and permissions.
1759
+ *
1760
+ * See {@link PermissionsRegistryService}
1761
+ * and {@link https://backstage.io/docs/backend-system/core-services/permission-integrations | the service docs}
1762
+ * for more information.
1763
+ *
1764
+ * @public
1765
+ */
1766
+ const permissionsRegistry: ServiceRef<PermissionsRegistryService, "plugin", "singleton">;
1600
1767
  /**
1601
1768
  * Built-in service for accessing metadata about the current plugin.
1602
1769
  *
@@ -1845,4 +2012,4 @@ declare function createBackendFeatureLoader<TDeps extends {
1845
2012
  [name in string]: unknown;
1846
2013
  }>(options: CreateBackendFeatureLoaderOptions<TDeps>): BackendFeature;
1847
2014
 
1848
- export { type AuthService, type BackendFeature, type BackendModuleRegistrationPoints, type BackendPluginRegistrationPoints, type BackstageCredentials, type BackstageNonePrincipal, type BackstagePrincipalAccessRestrictions, type BackstagePrincipalTypes, type BackstageServicePrincipal, type BackstageUserInfo, type BackstageUserPrincipal, type CacheService, type CacheServiceOptions, type CacheServiceSetOptions, type CreateBackendFeatureLoaderOptions, type CreateBackendModuleOptions, type CreateBackendPluginOptions, type CreateExtensionPointOptions, type DatabaseService, type DiscoveryService, type ExtensionPoint, type HttpAuthService, type HttpRouterService, type HttpRouterServiceAuthPolicy, type LifecycleService, type LifecycleServiceShutdownHook, type LifecycleServiceShutdownOptions, type LifecycleServiceStartupHook, type LifecycleServiceStartupOptions, type LoggerService, type PermissionsService, type PermissionsServiceRequestOptions, type PluginMetadataService, type PluginServiceFactoryOptions, type RootConfigService, type RootHealthService, type RootHttpRouterService, type RootLifecycleService, type RootLoggerService, type RootServiceFactoryOptions, type SchedulerService, type SchedulerServiceTaskDescriptor, type SchedulerServiceTaskFunction, type SchedulerServiceTaskInvocationDefinition, type SchedulerServiceTaskRunner, type SchedulerServiceTaskScheduleDefinition, type SchedulerServiceTaskScheduleDefinitionConfig, type ServiceFactory, type ServiceRef, type ServiceRefOptions, type UrlReaderService, type UrlReaderServiceReadTreeOptions, type UrlReaderServiceReadTreeResponse, type UrlReaderServiceReadTreeResponseDirOptions, type UrlReaderServiceReadTreeResponseFile, type UrlReaderServiceReadUrlOptions, type UrlReaderServiceReadUrlResponse, type UrlReaderServiceSearchOptions, type UrlReaderServiceSearchResponse, type UrlReaderServiceSearchResponseFile, type UserInfoService, coreServices, createBackendFeatureLoader, createBackendModule, createBackendPlugin, createExtensionPoint, createServiceFactory, createServiceRef, isDatabaseConflictError, readSchedulerServiceTaskScheduleDefinitionFromConfig, resolvePackagePath, resolveSafeChildPath };
2015
+ export { type AuditorService, type AuditorServiceCreateEventOptions, type AuditorServiceEvent, type AuditorServiceEventSeverityLevel, type AuthService, type BackendFeature, type BackendModuleRegistrationPoints, type BackendPluginRegistrationPoints, type BackstageCredentials, type BackstageNonePrincipal, type BackstagePrincipalAccessRestrictions, type BackstagePrincipalTypes, type BackstageServicePrincipal, type BackstageUserInfo, type BackstageUserPrincipal, type CacheService, type CacheServiceOptions, type CacheServiceSetOptions, type CreateBackendFeatureLoaderOptions, type CreateBackendModuleOptions, type CreateBackendPluginOptions, type CreateExtensionPointOptions, type DatabaseService, type DiscoveryService, type ExtensionPoint, type HttpAuthService, type HttpRouterService, type HttpRouterServiceAuthPolicy, type LifecycleService, type LifecycleServiceShutdownHook, type LifecycleServiceShutdownOptions, type LifecycleServiceStartupHook, type LifecycleServiceStartupOptions, type LoggerService, type PermissionsRegistryService, type PermissionsRegistryServiceAddResourceTypeOptions, type PermissionsService, type PermissionsServiceRequestOptions, type PluginMetadataService, type PluginServiceFactoryOptions, type RootConfigService, type RootHealthService, type RootHttpRouterService, type RootLifecycleService, type RootLoggerService, type RootServiceFactoryOptions, type SchedulerService, type SchedulerServiceTaskDescriptor, type SchedulerServiceTaskFunction, type SchedulerServiceTaskInvocationDefinition, type SchedulerServiceTaskRunner, type SchedulerServiceTaskScheduleDefinition, type SchedulerServiceTaskScheduleDefinitionConfig, type ServiceFactory, type ServiceRef, type ServiceRefOptions, type UrlReaderService, type UrlReaderServiceReadTreeOptions, type UrlReaderServiceReadTreeResponse, type UrlReaderServiceReadTreeResponseDirOptions, type UrlReaderServiceReadTreeResponseFile, type UrlReaderServiceReadUrlOptions, type UrlReaderServiceReadUrlResponse, type UrlReaderServiceSearchOptions, type UrlReaderServiceSearchResponse, type UrlReaderServiceSearchResponseFile, type UserInfoService, coreServices, createBackendFeatureLoader, createBackendModule, createBackendPlugin, createExtensionPoint, createServiceFactory, createServiceRef, isDatabaseConflictError, readSchedulerServiceTaskScheduleDefinitionFromConfig, resolvePackagePath, resolveSafeChildPath };