@backstage/plugin-catalog-backend-module-incremental-ingestion 0.0.0-nightly-20221128023335 → 0.1.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/CHANGELOG.md +21 -11
- package/alpha/package.json +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/package.json +14 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend-module-incremental-ingestion
|
|
2
2
|
|
|
3
|
-
## 0.0.0
|
|
3
|
+
## 0.1.0-next.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
@@ -8,14 +8,24 @@
|
|
|
8
8
|
|
|
9
9
|
### Patch Changes
|
|
10
10
|
|
|
11
|
+
- de8a975911: Changed to use native `AbortController` and `AbortSignal` from Node.js, instead
|
|
12
|
+
of the one from `node-abort-controller`. This is possible now that the minimum
|
|
13
|
+
supported Node.js version of the project is 16.
|
|
14
|
+
|
|
15
|
+
Note that their interfaces are very slightly different, but typically not in a
|
|
16
|
+
way that matters to consumers. If you see any typescript errors as a direct
|
|
17
|
+
result from this, they are compatible with each other in the ways that we
|
|
18
|
+
interact with them, and should be possible to type-cast across without ill
|
|
19
|
+
effects.
|
|
20
|
+
|
|
11
21
|
- Updated dependencies
|
|
12
|
-
- @backstage/
|
|
13
|
-
- @backstage/backend
|
|
14
|
-
- @backstage/backend-
|
|
15
|
-
- @backstage/plugin-
|
|
16
|
-
- @backstage/backend-
|
|
17
|
-
- @backstage/plugin-catalog-node@
|
|
18
|
-
- @backstage/
|
|
19
|
-
- @backstage/catalog-model@
|
|
20
|
-
- @backstage/
|
|
21
|
-
- @backstage/
|
|
22
|
+
- @backstage/backend-common@0.17.0-next.1
|
|
23
|
+
- @backstage/plugin-catalog-backend@1.6.0-next.1
|
|
24
|
+
- @backstage/backend-tasks@0.4.0-next.1
|
|
25
|
+
- @backstage/backend-plugin-api@0.1.5-next.1
|
|
26
|
+
- @backstage/backend-test-utils@0.1.31-next.1
|
|
27
|
+
- @backstage/plugin-catalog-node@1.2.2-next.1
|
|
28
|
+
- @backstage/config@1.0.5-next.1
|
|
29
|
+
- @backstage/catalog-model@1.1.4-next.1
|
|
30
|
+
- @backstage/errors@1.1.4-next.1
|
|
31
|
+
- @backstage/plugin-permission-common@0.7.2-next.1
|
package/alpha/package.json
CHANGED
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/types.ts","../src/database/IncrementalIngestionDatabaseManager.ts","../src/database/tables.ts","../src/database/migrations.ts","../src/engine/IncrementalIngestionEngine.ts","../src/router/paths.ts","../src/router/routes.ts","../src/util.ts","../src/module/WrapperProviders.ts","../src/module/incrementalIngestionEntityProviderCatalogModule.ts","../src/service/IncrementalCatalogBuilder.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n PluginDatabaseManager,\n UrlReader,\n} from '@backstage/backend-common';\nimport type {\n PluginTaskScheduler,\n TaskFunction,\n} from '@backstage/backend-tasks';\nimport type { Config } from '@backstage/config';\nimport type {\n DeferredEntity,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-backend';\nimport type { PermissionEvaluator } from '@backstage/plugin-permission-common';\nimport type { DurationObjectUnits } from 'luxon';\nimport type { Logger } from 'winston';\nimport { IncrementalIngestionDatabaseManager } from './database/IncrementalIngestionDatabaseManager';\n\n/**\n * Entity annotation containing the incremental entity provider.\n *\n * @public\n */\nexport const INCREMENTAL_ENTITY_PROVIDER_ANNOTATION =\n 'backstage.io/incremental-provider-name';\n\n/**\n * Ingest entities into the catalog in bite-sized chunks.\n *\n * A Normal `EntityProvider` allows you to introduce entities into the\n * processing pipeline by calling an `applyMutation()` on the full set\n * of entities. However, this is not great when the number of entities\n * that you have to keep track of is extremely large because it\n * entails having all of them in memory at once. An\n * `IncrementalEntityProvider` by contrast allows you to provide\n * batches of entities in sequence so that you never need to have more\n * than a few hundred in memory at a time.\n *\n * @public\n */\nexport interface IncrementalEntityProvider<TCursor, TContext> {\n /**\n * This name must be unique between all of the entity providers\n * operating in the catalog.\n */\n getProviderName(): string;\n\n /**\n * Return a single page of entities from a specific point in the\n * ingestion.\n *\n * @param context - anything needed in order to fetch a single page.\n * @param cursor - a unique value identifying the page to ingest.\n * @returns The entities to be ingested, as well as the cursor of\n * the next page after this one.\n */\n next(\n context: TContext,\n cursor?: TCursor,\n ): Promise<EntityIteratorResult<TCursor>>;\n\n /**\n * Do any setup and teardown necessary in order to provide the\n * context for fetching pages. This should always invoke `burst` in\n * order to fetch the individual pages.\n *\n * @param burst - a function which performs a series of iterations\n */\n around(burst: (context: TContext) => Promise<void>): Promise<void>;\n}\n\n/**\n * Value returned by an {@link IncrementalEntityProvider} to provide a\n * single page of entities to ingest.\n *\n * @public\n */\nexport type EntityIteratorResult<T> =\n | {\n done: false;\n entities: DeferredEntity[];\n cursor: T;\n }\n | {\n done: true;\n entities?: DeferredEntity[];\n cursor?: T;\n };\n\n/** @public */\nexport interface IncrementalEntityProviderOptions {\n /**\n * Entities are ingested in bursts. This interval determines how\n * much time to wait in between each burst.\n */\n burstInterval: DurationObjectUnits;\n\n /**\n * Entities are ingested in bursts. This value determines how long\n * to keep ingesting within each burst.\n */\n burstLength: DurationObjectUnits;\n\n /**\n * After a successful ingestion, the incremental entity provider\n * will rest for this period of time before starting to ingest\n * again.\n */\n restLength: DurationObjectUnits;\n\n /**\n * In the event of an error during an ingestion burst, the backoff\n * determines how soon it will be retried. E.g.\n * `[{ minutes: 1}, { minutes: 5}, {minutes: 30 }, { hours: 3 }]`\n */\n backoff?: DurationObjectUnits[];\n}\n\n/** @public */\nexport type PluginEnvironment = {\n logger: Logger;\n database: PluginDatabaseManager;\n scheduler: PluginTaskScheduler;\n config: Config;\n reader: UrlReader;\n permissions: PermissionEvaluator;\n};\n\nexport interface IterationEngine {\n taskFn: TaskFunction;\n}\n\nexport interface IterationEngineOptions {\n logger: Logger;\n connection: EntityProviderConnection;\n manager: IncrementalIngestionDatabaseManager;\n provider: IncrementalEntityProvider<unknown, unknown>;\n restLength: DurationObjectUnits;\n ready: Promise<void>;\n backoff?: IncrementalEntityProviderOptions['backoff'];\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport type { DeferredEntity } from '@backstage/plugin-catalog-backend';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { Duration } from 'luxon';\nimport { v4 } from 'uuid';\nimport { INCREMENTAL_ENTITY_PROVIDER_ANNOTATION } from '../types';\nimport {\n IngestionRecord,\n IngestionRecordUpdate,\n IngestionUpsert,\n MarkRecord,\n MarkRecordInsert,\n} from './tables';\n\nexport class IncrementalIngestionDatabaseManager {\n private client: Knex;\n\n constructor(options: { client: Knex }) {\n this.client = options.client;\n }\n\n /**\n * Performs an update to the ingestion record with matching `id`.\n * @param options - IngestionRecordUpdate\n */\n async updateIngestionRecordById(options: IngestionRecordUpdate) {\n await this.client.transaction(async tx => {\n const { ingestionId, update } = options;\n await tx('ingestions').where('id', ingestionId).update(update);\n });\n }\n\n /**\n * Performs an update to the ingestion record with matching provider name. Will only update active records.\n * @param provider - string\n * @param update - Partial<IngestionUpsertIFace>\n */\n async updateIngestionRecordByProvider(\n provider: string,\n update: Partial<IngestionUpsert>,\n ) {\n await this.client.transaction(async tx => {\n await tx('ingestions')\n .where('provider_name', provider)\n .andWhere('completion_ticket', 'open')\n .update(update);\n });\n }\n\n /**\n * Performs an insert into the `ingestions` table with the supplied values.\n * @param record - IngestionUpsertIFace\n */\n async insertIngestionRecord(record: IngestionUpsert) {\n await this.client.transaction(async tx => {\n await tx('ingestions').insert(record);\n });\n }\n\n private async deleteMarkEntities(\n tx: Knex.Transaction,\n ids: { id: string }[],\n ) {\n const chunks: { id: string }[][] = [];\n for (let i = 0; i < ids.length; i += 100) {\n const chunk = ids.slice(i, i + 100);\n chunks.push(chunk);\n }\n\n let deleted = 0;\n\n for (const chunk of chunks) {\n const chunkDeleted = await tx('ingestion_mark_entities')\n .delete()\n .whereIn(\n 'id',\n chunk.map(entry => entry.id),\n );\n deleted += chunkDeleted;\n }\n\n return deleted;\n }\n\n /**\n * Finds the current ingestion record for the named provider.\n * @param provider - string\n * @returns IngestionRecord | undefined\n */\n async getCurrentIngestionRecord(provider: string) {\n return await this.client.transaction(async tx => {\n const record = await tx<IngestionRecord>('ingestions')\n .where('provider_name', provider)\n .andWhere('completion_ticket', 'open')\n .first();\n return record;\n });\n }\n\n /**\n * Removes all entries from `ingestion_marks_entities`, `ingestion_marks`, and `ingestions`\n * for prior ingestions that completed (i.e., have a `completion_ticket` value other than 'open').\n * @param provider - string\n * @returns A count of deletions for each record type.\n */\n async clearFinishedIngestions(provider: string) {\n return await this.client.transaction(async tx => {\n const markEntitiesDeleted = await tx('ingestion_mark_entities')\n .delete()\n .whereIn(\n 'ingestion_mark_id',\n tx('ingestion_marks')\n .select('id')\n .whereIn(\n 'ingestion_id',\n tx('ingestions')\n .select('id')\n .where('provider_name', provider)\n .andWhereNot('completion_ticket', 'open'),\n ),\n );\n\n const marksDeleted = await tx('ingestion_marks')\n .delete()\n .whereIn(\n 'ingestion_id',\n tx('ingestions')\n .select('id')\n .where('provider_name', provider)\n .andWhereNot('completion_ticket', 'open'),\n );\n\n const ingestionsDeleted = await tx('ingestions')\n .delete()\n .where('provider_name', provider)\n .andWhereNot('completion_ticket', 'open');\n\n return {\n deletions: {\n markEntitiesDeleted,\n marksDeleted,\n ingestionsDeleted,\n },\n };\n });\n }\n\n /**\n * Automatically cleans up duplicate ingestion records if they were accidentally created.\n * Any ingestion record where the `rest_completed_at` is null (meaning it is active) AND\n * the ingestionId is incorrect is a duplicate ingestion record.\n * @param ingestionId - string\n * @param provider - string\n */\n async clearDuplicateIngestions(ingestionId: string, provider: string) {\n await this.client.transaction(async tx => {\n const invalid = await tx<IngestionRecord>('ingestions')\n .where('provider_name', provider)\n .andWhere('rest_completed_at', null)\n .andWhereNot('id', ingestionId);\n\n if (invalid.length > 0) {\n await tx('ingestions').delete().whereIn('id', invalid);\n await tx('ingestion_mark_entities')\n .delete()\n .whereIn(\n 'ingestion_mark_id',\n tx('ingestion_marks').select('id').whereIn('ingestion_id', invalid),\n );\n await tx('ingestion_marks').delete().whereIn('ingestion_id', invalid);\n }\n });\n }\n\n /**\n * This method fully purges and resets all ingestion records for the named provider, and\n * leaves it in a paused state.\n * @param provider - string\n * @returns Counts of all deleted ingestion records\n */\n async purgeAndResetProvider(provider: string) {\n return await this.client.transaction(async tx => {\n const ingestionIDs: { id: string }[] = await tx('ingestions')\n .select('id')\n .where('provider_name', provider);\n\n const markIDs: { id: string }[] =\n ingestionIDs.length > 0\n ? await tx('ingestion_marks')\n .select('id')\n .whereIn(\n 'ingestion_id',\n ingestionIDs.map(entry => entry.id),\n )\n : [];\n\n const markEntityIDs: { id: string }[] =\n markIDs.length > 0\n ? await tx('ingestion_mark_entities')\n .select('id')\n .whereIn(\n 'ingestion_mark_id',\n markIDs.map(entry => entry.id),\n )\n : [];\n\n const markEntitiesDeleted = await this.deleteMarkEntities(\n tx,\n markEntityIDs,\n );\n\n const marksDeleted =\n markIDs.length > 0\n ? await tx('ingestion_marks')\n .delete()\n .whereIn(\n 'ingestion_id',\n ingestionIDs.map(entry => entry.id),\n )\n : 0;\n\n const ingestionsDeleted = await tx('ingestions')\n .delete()\n .where('provider_name', provider);\n\n const next_action_at = new Date();\n next_action_at.setTime(next_action_at.getTime() + 24 * 60 * 60 * 1000);\n\n await this.insertIngestionRecord({\n id: v4(),\n next_action: 'rest',\n provider_name: provider,\n next_action_at,\n ingestion_completed_at: new Date(),\n status: 'resting',\n completion_ticket: 'open',\n });\n\n return { provider, ingestionsDeleted, marksDeleted, markEntitiesDeleted };\n });\n }\n\n /**\n * Creates a new ingestion record.\n * @param provider - string\n * @returns A new ingestion record\n */\n async createProviderIngestionRecord(provider: string) {\n const ingestionId = v4();\n const nextAction = 'ingest';\n try {\n await this.insertIngestionRecord({\n id: ingestionId,\n next_action: nextAction,\n provider_name: provider,\n status: 'bursting',\n completion_ticket: 'open',\n });\n return { ingestionId, nextAction, attempts: 0, nextActionAt: Date.now() };\n } catch (_e) {\n // Creating the ingestion record failed. Return undefined.\n return undefined;\n }\n }\n\n /**\n * Computes which entities to remove, if any, at the end of a burst.\n * @param provider - string\n * @param ingestionId - string\n * @returns All entities to remove for this burst.\n */\n async computeRemoved(provider: string, ingestionId: string) {\n return await this.client.transaction(async tx => {\n const removed: { entity: string; ref: string }[] = await tx(\n 'final_entities',\n )\n .select(\n tx.ref('final_entity').as('entity'),\n tx.ref('refresh_state.entity_ref').as('ref'),\n )\n .join(\n 'refresh_state',\n 'refresh_state.entity_id',\n 'final_entities.entity_id',\n )\n .join('search', 'search.entity_id', 'final_entities.entity_id')\n .whereNotIn(\n 'entity_ref',\n tx('ingestion_marks')\n .join(\n 'ingestion_mark_entities',\n 'ingestion_marks.id',\n 'ingestion_mark_entities.ingestion_mark_id',\n )\n .select('ingestion_mark_entities.ref')\n .where('ingestion_marks.ingestion_id', ingestionId),\n )\n .andWhere(\n 'search.key',\n `metadata.annotations.${INCREMENTAL_ENTITY_PROVIDER_ANNOTATION}`,\n )\n .andWhere('search.value', provider);\n return removed.map(entity => {\n return { entity: JSON.parse(entity.entity) };\n });\n });\n }\n\n /**\n * Performs a lookup of all providers that have duplicate active ingestion records.\n * @returns An array of all duplicate active ingestions\n */\n async healthcheck() {\n return await this.client.transaction(async tx => {\n const records = await tx<{ id: string; provider_name: string }>(\n 'ingestions',\n )\n .distinct('id', 'provider_name')\n .where('rest_completed_at', null);\n return records;\n });\n }\n\n /**\n * Skips any wait time for the next action to run.\n * @param provider - string\n */\n async triggerNextProviderAction(provider: string) {\n await this.updateIngestionRecordByProvider(provider, {\n next_action_at: new Date(),\n });\n }\n\n /**\n * Purges the following tables:\n * * `ingestions`\n * * `ingestion_marks`\n * * `ingestion_mark_entities`\n *\n * This function leaves the ingestions table with all providers in a paused state.\n * @returns Results from cleaning up all ingestion tables.\n */\n async cleanupProviders() {\n const providers = await this.listProviders();\n\n const ingestionsDeleted = await this.purgeTable('ingestions');\n\n const next_action_at = new Date();\n next_action_at.setTime(next_action_at.getTime() + 24 * 60 * 60 * 1000);\n\n for (const provider of providers) {\n await this.insertIngestionRecord({\n id: v4(),\n next_action: 'rest',\n provider_name: provider,\n next_action_at,\n ingestion_completed_at: new Date(),\n status: 'resting',\n completion_ticket: 'open',\n });\n }\n\n const ingestionMarksDeleted = await this.purgeTable('ingestion_marks');\n const markEntitiesDeleted = await this.purgeTable(\n 'ingestion_mark_entities',\n );\n\n return { ingestionsDeleted, ingestionMarksDeleted, markEntitiesDeleted };\n }\n\n /**\n * Configures the current ingestion record to ingest a burst.\n * @param ingestionId - string\n */\n async setProviderIngesting(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: { next_action: 'ingest' },\n });\n }\n\n /**\n * Indicates the provider is currently ingesting a burst.\n * @param ingestionId - string\n */\n async setProviderBursting(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: { status: 'bursting' },\n });\n }\n\n /**\n * Finalizes the current ingestion record to indicate that the post-ingestion rest period is complete.\n * @param ingestionId - string\n */\n async setProviderComplete(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: {\n next_action: 'nothing (done)',\n rest_completed_at: new Date(),\n status: 'complete',\n completion_ticket: v4(),\n },\n });\n }\n\n /**\n * Marks ingestion as complete and starts the post-ingestion rest cycle.\n * @param ingestionId - string\n * @param restLength - Duration\n */\n async setProviderResting(ingestionId: string, restLength: Duration) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: {\n next_action: 'rest',\n next_action_at: new Date(Date.now() + restLength.as('milliseconds')),\n ingestion_completed_at: new Date(),\n status: 'resting',\n },\n });\n }\n\n /**\n * Marks ingestion as paused after a burst completes.\n * @param ingestionId - string\n */\n async setProviderInterstitial(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: { attempts: 0, status: 'interstitial' },\n });\n }\n\n /**\n * Starts the cancel process for the current ingestion.\n * @param ingestionId - string\n * @param message - string (optional)\n */\n async setProviderCanceling(ingestionId: string, message?: string) {\n const update: Partial<IngestionUpsert> = {\n next_action: 'cancel',\n last_error: message ? message : undefined,\n next_action_at: new Date(),\n status: 'canceling',\n };\n await this.updateIngestionRecordById({ ingestionId, update });\n }\n\n /**\n * Completes the cancel process and triggers a new ingestion.\n * @param ingestionId - string\n */\n async setProviderCanceled(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: {\n next_action: 'nothing (canceled)',\n rest_completed_at: new Date(),\n status: 'complete',\n completion_ticket: v4(),\n },\n });\n }\n\n /**\n * Configures the current ingestion to wait and retry, due to a data source error.\n * @param ingestionId - string\n * @param attempts - number\n * @param error - Error\n * @param backoffLength - number\n */\n async setProviderBackoff(\n ingestionId: string,\n attempts: number,\n error: Error,\n backoffLength: number,\n ) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: {\n next_action: 'backoff',\n attempts: attempts + 1,\n last_error: String(error),\n next_action_at: new Date(Date.now() + backoffLength),\n status: 'backing off',\n },\n });\n }\n\n /**\n * Returns the last record from `ingestion_marks` for the supplied ingestionId.\n * @param ingestionId - string\n * @returns MarkRecord | undefined\n */\n async getLastMark(ingestionId: string) {\n return await this.client.transaction(async tx => {\n const mark = await tx<MarkRecord>('ingestion_marks')\n .where('ingestion_id', ingestionId)\n .orderBy('sequence', 'desc')\n .first();\n return mark;\n });\n }\n\n async getAllMarks(ingestionId: string) {\n return await this.client.transaction(async tx => {\n const marks = await tx<MarkRecord>('ingestion_marks')\n .where('ingestion_id', ingestionId)\n .orderBy('sequence', 'desc');\n return marks;\n });\n }\n\n /**\n * Performs an insert into the `ingestion_marks` table with the supplied values.\n * @param options - MarkRecordInsert\n */\n async createMark(options: MarkRecordInsert) {\n const { record } = options;\n await this.client.transaction(async tx => {\n await tx('ingestion_marks').insert(record);\n });\n }\n /**\n * Performs an upsert to the `ingestion_mark_entities` table for all deferred entities.\n * @param markId - string\n * @param entities - DeferredEntity[]\n */\n async createMarkEntities(markId: string, entities: DeferredEntity[]) {\n await this.client.transaction(async tx => {\n await tx('ingestion_mark_entities').insert(\n entities.map(entity => ({\n id: v4(),\n ingestion_mark_id: markId,\n ref: stringifyEntityRef(entity.entity),\n })),\n );\n });\n }\n\n /**\n * Deletes the entire content of a table, and returns the number of records deleted.\n * @param table - string\n * @returns number\n */\n async purgeTable(table: string) {\n return await this.client.transaction(async tx => {\n return await tx(table).delete();\n });\n }\n\n /**\n * Returns a list of all providers.\n * @returns string[]\n */\n async listProviders() {\n return await this.client.transaction(async tx => {\n const providers = await tx<{ provider_name: string }>(\n 'ingestions',\n ).distinct('provider_name');\n return providers.map(entry => entry.provider_name);\n });\n }\n\n async updateByName(provider: string, update: Partial<IngestionUpsert>) {\n await this.updateIngestionRecordByProvider(provider, update);\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport const DB_MIGRATIONS_TABLE = 'incremental_ingestion__knex_migrations';\n\n/**\n * The shape of data inserted into or updated in the `ingestions` table.\n */\nexport interface IngestionUpsert {\n /**\n * The ingestion record id.\n */\n id?: string;\n /**\n * The next action the incremental entity provider will take.\n */\n next_action:\n | 'rest'\n | 'ingest'\n | 'backoff'\n | 'cancel'\n | 'nothing (done)'\n | 'nothing (canceled)';\n /**\n * Current status of the incremental entity provider.\n */\n status:\n | 'complete'\n | 'bursting'\n | 'resting'\n | 'canceling'\n | 'interstitial'\n | 'backing off';\n /**\n * The name of the incremental entity provider being updated.\n */\n provider_name: string;\n /**\n * Date/time stamp for when the next action will trigger.\n */\n next_action_at?: Date;\n /**\n * A record of the last error generated by the incremental entity provider.\n */\n last_error?: string | null;\n /**\n * The number of attempts the provider has attempted during the current cycle.\n */\n attempts?: number;\n /**\n * Date/time stamp for the completion of ingestion.\n */\n ingestion_completed_at?: Date | string | null;\n /**\n * Date/time stamp for the end of the rest cycle before the next ingestion.\n */\n rest_completed_at?: Date | string | null;\n /**\n * A record of the finalized status of the ingestion record. Values are either 'open' or a uuid.\n */\n completion_ticket: string;\n}\n\n/**\n * This interface is for updating an existing ingestion record.\n */\nexport interface IngestionRecordUpdate {\n ingestionId: string;\n update: Partial<IngestionUpsert>;\n}\n\n/**\n * The expected response from the `ingestion_marks` table.\n */\nexport interface MarkRecord {\n id: string;\n sequence: number;\n ingestion_id: string;\n cursor: string;\n created_at: string;\n}\n\n/**\n * The expected response from the `ingestions` table.\n */\nexport interface IngestionRecord extends IngestionUpsert {\n id: string;\n next_action_at: Date;\n /**\n * The date/time the ingestion record was created.\n */\n created_at: string;\n}\n\n/**\n * This interface supplies all the values for adding an ingestion mark.\n */\nexport interface MarkRecordInsert {\n record: {\n id: string;\n ingestion_id: string;\n cursor: unknown;\n sequence: number;\n };\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { resolvePackagePath } from '@backstage/backend-common';\nimport { Knex } from 'knex';\nimport { DB_MIGRATIONS_TABLE } from './tables';\n\nexport async function applyDatabaseMigrations(knex: Knex): Promise<void> {\n const migrationsDir = resolvePackagePath(\n '@backstage/plugin-catalog-backend-module-incremental-ingestion',\n 'migrations',\n );\n\n await knex.migrate.latest({\n directory: migrationsDir,\n tableName: DB_MIGRATIONS_TABLE,\n });\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { DeferredEntity } from '@backstage/plugin-catalog-backend';\nimport {\n INCREMENTAL_ENTITY_PROVIDER_ANNOTATION,\n IterationEngine,\n IterationEngineOptions,\n} from '../types';\nimport { IncrementalIngestionDatabaseManager } from '../database/IncrementalIngestionDatabaseManager';\nimport type { AbortSignal } from 'node-abort-controller';\nimport { performance } from 'perf_hooks';\nimport { Duration, DurationObjectUnits } from 'luxon';\nimport { v4 } from 'uuid';\nimport { stringifyError } from '@backstage/errors';\n\nexport class IncrementalIngestionEngine implements IterationEngine {\n private readonly restLength: Duration;\n private readonly backoff: DurationObjectUnits[];\n\n private manager: IncrementalIngestionDatabaseManager;\n\n constructor(private options: IterationEngineOptions) {\n this.manager = options.manager;\n this.restLength = Duration.fromObject(options.restLength);\n this.backoff = options.backoff ?? [\n { minutes: 1 },\n { minutes: 5 },\n { minutes: 30 },\n { hours: 3 },\n ];\n }\n\n async taskFn(signal: AbortSignal) {\n try {\n this.options.logger.debug('Begin tick');\n await this.handleNextAction(signal);\n } catch (error) {\n this.options.logger.error(`${error}`);\n throw error;\n } finally {\n this.options.logger.debug('End tick');\n }\n }\n\n async handleNextAction(signal: AbortSignal) {\n await this.options.ready;\n\n const result = await this.getCurrentAction();\n if (result) {\n const { ingestionId, nextActionAt, nextAction, attempts } = result;\n\n switch (nextAction) {\n case 'rest':\n if (Date.now() > nextActionAt) {\n await this.manager.clearFinishedIngestions(\n this.options.provider.getProviderName(),\n );\n this.options.logger.info(\n `incremental-engine: Ingestion ${ingestionId} rest period complete. Ingestion will start again`,\n );\n\n await this.manager.setProviderComplete(ingestionId);\n } else {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' rest period continuing`,\n );\n }\n break;\n case 'ingest':\n try {\n await this.manager.setProviderBursting(ingestionId);\n const done = await this.ingestOneBurst(ingestionId, signal);\n if (done) {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' complete, transitioning to rest period of ${this.restLength.toHuman()}`,\n );\n await this.manager.setProviderResting(\n ingestionId,\n this.restLength,\n );\n } else {\n await this.manager.setProviderInterstitial(ingestionId);\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' continuing`,\n );\n }\n } catch (error) {\n if (\n (error as Error).message &&\n (error as Error).message === 'CANCEL'\n ) {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' canceled`,\n );\n await this.manager.setProviderCanceling(\n ingestionId,\n (error as Error).message,\n );\n } else {\n const currentBackoff = Duration.fromObject(\n this.backoff[Math.min(this.backoff.length - 1, attempts)],\n );\n\n const backoffLength = currentBackoff.as('milliseconds');\n this.options.logger.error(error);\n\n const truncatedError = stringifyError(error).substring(0, 700);\n this.options.logger.error(\n `incremental-engine: Ingestion '${ingestionId}' threw an error during ingestion burst. Ingestion will backoff for ${currentBackoff.toHuman()} (${truncatedError})`,\n );\n\n await this.manager.setProviderBackoff(\n ingestionId,\n attempts,\n error as Error,\n backoffLength,\n );\n }\n }\n break;\n case 'backoff':\n if (Date.now() > nextActionAt) {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' backoff complete, will attempt to resume`,\n );\n await this.manager.setProviderIngesting(ingestionId);\n } else {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' backoff continuing`,\n );\n }\n break;\n case 'cancel':\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' canceling, will restart`,\n );\n await this.manager.setProviderCanceled(ingestionId);\n break;\n default:\n this.options.logger.error(\n `incremental-engine: Ingestion '${ingestionId}' received unknown action '${nextAction}'`,\n );\n }\n } else {\n this.options.logger.error(\n `incremental-engine: Engine tried to create duplicate ingestion record for provider '${this.options.provider.getProviderName()}'.`,\n );\n }\n }\n\n async getCurrentAction() {\n const providerName = this.options.provider.getProviderName();\n const record = await this.manager.getCurrentIngestionRecord(providerName);\n if (record) {\n this.options.logger.info(\n `incremental-engine: Ingestion record found: '${record.id}'`,\n );\n return {\n ingestionId: record.id,\n nextAction: record.next_action as 'rest' | 'ingest' | 'backoff',\n attempts: record.attempts as number,\n nextActionAt: record.next_action_at.valueOf() as number,\n };\n }\n const result = await this.manager.createProviderIngestionRecord(\n providerName,\n );\n if (result) {\n this.options.logger.info(\n `incremental-engine: Ingestion record created: '${result.ingestionId}'`,\n );\n }\n return result;\n }\n\n async ingestOneBurst(id: string, signal: AbortSignal) {\n const lastMark = await this.manager.getLastMark(id);\n\n const cursor = lastMark ? lastMark.cursor : undefined;\n let sequence = lastMark ? lastMark.sequence + 1 : 0;\n\n const start = performance.now();\n let count = 0;\n let done = false;\n this.options.logger.info(\n `incremental-engine: Ingestion '${id}' burst initiated`,\n );\n\n await this.options.provider.around(async (context: unknown) => {\n let next = await this.options.provider.next(context, cursor);\n count++;\n for (;;) {\n done = next.done;\n await this.mark({\n id,\n sequence,\n entities: next?.entities,\n done: next.done,\n cursor: next?.cursor,\n });\n if (signal.aborted || next.done) {\n break;\n } else {\n next = await this.options.provider.next(context, next.cursor);\n count++;\n sequence++;\n }\n }\n });\n\n this.options.logger.info(\n `incremental-engine: Ingestion '${id}' burst complete. (${count} batches in ${Math.round(\n performance.now() - start,\n )}ms).`,\n );\n return done;\n }\n\n async mark(options: {\n id: string;\n sequence: number;\n entities?: DeferredEntity[];\n done: boolean;\n cursor?: unknown;\n }) {\n const { id, sequence, entities, done, cursor } = options;\n this.options.logger.debug(\n `incremental-engine: Ingestion '${id}': MARK ${\n entities ? entities.length : 0\n } entities, cursor: ${\n cursor ? JSON.stringify(cursor) : 'none'\n }, done: ${done}`,\n );\n const markId = v4();\n\n await this.manager.createMark({\n record: {\n id: markId,\n ingestion_id: id,\n cursor,\n sequence,\n },\n });\n\n if (entities && entities.length > 0) {\n await this.manager.createMarkEntities(markId, entities);\n }\n\n const added =\n entities?.map(deferred => ({\n ...deferred,\n entity: {\n ...deferred.entity,\n metadata: {\n ...deferred.entity.metadata,\n annotations: {\n ...deferred.entity.metadata.annotations,\n [INCREMENTAL_ENTITY_PROVIDER_ANNOTATION]:\n this.options.provider.getProviderName(),\n },\n },\n },\n })) ?? [];\n\n const removed: DeferredEntity[] = [];\n\n if (done) {\n removed.push(\n ...(await this.manager.computeRemoved(\n this.options.provider.getProviderName(),\n id,\n )),\n );\n }\n\n await this.options.connection.applyMutation({\n type: 'delta',\n added,\n removed,\n });\n }\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport const PROVIDER_CLEANUP = '/incremental/cleanup';\nexport const PROVIDER_HEALTH = '/incremental/health';\nexport const PROVIDER_BASE_PATH = '/incremental/providers/:provider';\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { errorHandler } from '@backstage/backend-common';\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { Logger } from 'winston';\nimport { IncrementalIngestionDatabaseManager } from '../database/IncrementalIngestionDatabaseManager';\nimport { PROVIDER_BASE_PATH, PROVIDER_CLEANUP, PROVIDER_HEALTH } from './paths';\n\nexport const createIncrementalProviderRouter = async (\n manager: IncrementalIngestionDatabaseManager,\n logger: Logger,\n) => {\n const router = Router();\n router.use(express.json());\n\n // Get the overall health of all incremental providers\n router.get(PROVIDER_HEALTH, async (_, res) => {\n const records = await manager.healthcheck();\n const providers = records.map(record => record.provider_name);\n const duplicates = [\n ...new Set(providers.filter((e, i, a) => a.indexOf(e) !== i)),\n ];\n\n if (duplicates.length > 0) {\n res.json({ healthy: false, duplicateIngestions: duplicates });\n } else {\n res.json({ healthy: true });\n }\n });\n\n // Clean up and pause all providers\n router.post(PROVIDER_CLEANUP, async (_, res) => {\n const result = await manager.cleanupProviders();\n res.json(result);\n });\n\n // Get basic status of the provider\n router.get(PROVIDER_BASE_PATH, async (req, res) => {\n const { provider } = req.params;\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n res.json({\n success: true,\n status: {\n current_action: record.status,\n next_action_at: new Date(record.next_action_at),\n },\n last_error: record.last_error,\n });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n res.json({\n success: true,\n status: {\n current_action: 'rest complete, waiting to start',\n },\n });\n } else {\n logger.error(\n `${provider} - No ingestion record found in the database!`,\n );\n res.status(404).json({\n success: false,\n status: {},\n last_error: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n // Trigger the provider's next action\n router.post(`${PROVIDER_BASE_PATH}/trigger`, async (req, res) => {\n const { provider } = req.params;\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n await manager.triggerNextProviderAction(provider);\n res.json({\n success: true,\n message: `${provider}: Next action triggered.`,\n });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n logger.debug(`${provider} - Ingestion record found`);\n res.json({\n success: true,\n message: 'Unable to trigger next action (provider is restarting)',\n });\n } else {\n res.status(404).json({\n success: false,\n message: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n // Start a brand-new ingestion cycle for the provider.\n // (Cancel's the current run if active, or marks it complete if resting)\n router.post(`${PROVIDER_BASE_PATH}/start`, async (req, res) => {\n const { provider } = req.params;\n\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n const ingestionId = record.id;\n if (record.status === 'resting') {\n await manager.setProviderComplete(ingestionId);\n } else {\n await manager.setProviderCanceling(ingestionId);\n }\n res.json({\n success: true,\n message: `${provider}: Next cycle triggered.`,\n });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n logger.debug(`${provider} - Ingestion record found`);\n res.json({\n success: true,\n message: 'Provider is already restarting',\n });\n } else {\n res.status(404).json({\n success: false,\n message: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n // Stop the provider and pause it for 24 hours\n router.post(`${PROVIDER_BASE_PATH}/cancel`, async (req, res) => {\n const { provider } = req.params;\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n const next_action_at = new Date();\n next_action_at.setTime(next_action_at.getTime() + 24 * 60 * 60 * 1000);\n await manager.updateByName(provider, {\n next_action: 'nothing (done)',\n ingestion_completed_at: new Date(),\n next_action_at,\n status: 'resting',\n });\n res.json({\n success: true,\n message: `${provider}: Current ingestion canceled.`,\n });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n logger.debug(`${provider} - Ingestion record found`);\n res.json({\n success: true,\n message: 'Provider is currently restarting, please wait.',\n });\n } else {\n res.status(404).json({\n success: false,\n message: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n // Wipe out all ingestion records for the provider and pause for 24 hours\n router.delete(PROVIDER_BASE_PATH, async (req, res) => {\n const { provider } = req.params;\n const result = await manager.purgeAndResetProvider(provider);\n res.json(result);\n });\n\n // Get the ingestion marks for the current cycle\n router.get(`${PROVIDER_BASE_PATH}/marks`, async (req, res) => {\n const { provider } = req.params;\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n const id = record.id;\n const records = await manager.getAllMarks(id);\n res.json({ success: true, records });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n logger.debug(`${provider} - Ingestion record found`);\n res.json({\n success: true,\n message: 'No records yet (provider is restarting)',\n });\n } else {\n logger.error(\n `${provider} - No ingestion record found in the database!`,\n );\n res.status(404).json({\n success: false,\n status: {},\n last_error: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n router.delete(`${PROVIDER_BASE_PATH}/marks`, async (req, res) => {\n const { provider } = req.params;\n const deletions = await manager.clearFinishedIngestions(provider);\n\n res.json({\n success: true,\n message: `Expired marks for provider '${provider}' removed.`,\n deletions,\n });\n });\n\n router.use(errorHandler());\n\n return router;\n};\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport class Deferred<T> implements Promise<T> {\n #resolve?: (value: T) => void;\n #reject?: (error: Error) => void;\n\n public get resolve() {\n return this.#resolve!;\n }\n public get reject() {\n return this.#reject!;\n }\n\n public then: Promise<T>['then'];\n public catch: Promise<T>['catch'];\n public finally: Promise<T>['finally'];\n\n public constructor() {\n const promise = new Promise<T>((resolve, reject) => {\n this.#resolve = resolve;\n this.#reject = reject;\n });\n\n this.then = promise.then.bind(promise);\n this.catch = promise.catch.bind(promise);\n this.finally = promise.finally.bind(promise);\n }\n\n [Symbol.toStringTag]: 'Deferred' = 'Deferred';\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Logger, loggerToWinstonLogger } from '@backstage/backend-plugin-api';\nimport { PluginTaskScheduler } from '@backstage/backend-tasks';\nimport { Config } from '@backstage/config';\nimport { stringifyError } from '@backstage/errors';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport express from 'express';\nimport { Knex } from 'knex';\nimport { Duration } from 'luxon';\nimport { IncrementalIngestionDatabaseManager } from '../database/IncrementalIngestionDatabaseManager';\nimport { applyDatabaseMigrations } from '../database/migrations';\nimport { IncrementalIngestionEngine } from '../engine/IncrementalIngestionEngine';\nimport { createIncrementalProviderRouter } from '../router/routes';\nimport {\n IncrementalEntityProvider,\n IncrementalEntityProviderOptions,\n} from '../types';\nimport { Deferred } from '../util';\n\n/**\n * Helps in the creation of the catalog entity providers that wrap the\n * incremental ones.\n */\nexport class WrapperProviders {\n private migrate: Promise<void> | undefined;\n private numberOfProvidersToConnect = 0;\n private readonly readySignal = new Deferred<void>();\n\n constructor(\n private readonly options: {\n config: Config;\n logger: Logger;\n client: Knex;\n scheduler: PluginTaskScheduler;\n applyDatabaseMigrations?: typeof applyDatabaseMigrations;\n },\n ) {}\n\n wrap(\n provider: IncrementalEntityProvider<unknown, unknown>,\n options: IncrementalEntityProviderOptions,\n ): EntityProvider {\n this.numberOfProvidersToConnect += 1;\n return {\n getProviderName: () => provider.getProviderName(),\n connect: async connection => {\n await this.startProvider(provider, options, connection);\n this.numberOfProvidersToConnect -= 1;\n if (this.numberOfProvidersToConnect === 0) {\n this.readySignal.resolve();\n }\n },\n };\n }\n\n async adminRouter(): Promise<express.Router> {\n return createIncrementalProviderRouter(\n new IncrementalIngestionDatabaseManager({ client: this.options.client }),\n loggerToWinstonLogger(this.options.logger),\n );\n }\n\n private async startProvider(\n provider: IncrementalEntityProvider<unknown, unknown>,\n providerOptions: IncrementalEntityProviderOptions,\n connection: EntityProviderConnection,\n ) {\n const logger = loggerToWinstonLogger(\n this.options.logger.child({\n entityProvider: provider.getProviderName(),\n }),\n );\n\n try {\n if (!this.migrate) {\n this.migrate = Promise.resolve().then(async () => {\n const apply =\n this.options.applyDatabaseMigrations ?? applyDatabaseMigrations;\n await apply(this.options.client);\n });\n }\n\n await this.migrate;\n\n const { burstInterval, burstLength, restLength } = providerOptions;\n\n logger.info(`Connecting`);\n\n const manager = new IncrementalIngestionDatabaseManager({\n client: this.options.client,\n });\n const engine = new IncrementalIngestionEngine({\n ...providerOptions,\n ready: this.readySignal,\n manager,\n logger,\n provider,\n restLength,\n connection,\n });\n\n const frequency = Duration.isDuration(burstInterval)\n ? burstInterval\n : Duration.fromObject(burstInterval);\n const length = Duration.isDuration(burstLength)\n ? burstLength\n : Duration.fromObject(burstLength);\n\n await this.options.scheduler.scheduleTask({\n id: provider.getProviderName(),\n fn: engine.taskFn.bind(engine),\n frequency,\n timeout: length,\n });\n } catch (error) {\n logger.warn(\n `Failed to initialize incremental ingestion provider ${provider.getProviderName()}, ${stringifyError(\n error,\n )}`,\n );\n throw error;\n }\n }\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configServiceRef,\n createBackendModule,\n databaseServiceRef,\n httpRouterServiceRef,\n loggerServiceRef,\n schedulerServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';\nimport {\n IncrementalEntityProvider,\n IncrementalEntityProviderOptions,\n} from '../types';\nimport { WrapperProviders } from './WrapperProviders';\n\n/**\n * Registers the incremental entity provider with the catalog processing extension point.\n *\n * @alpha\n */\nexport const incrementalIngestionEntityProviderCatalogModule =\n createBackendModule({\n pluginId: 'catalog',\n moduleId: 'incrementalIngestionEntityProvider',\n register(\n env,\n options: {\n providers: Array<{\n provider: IncrementalEntityProvider<unknown, unknown>;\n options: IncrementalEntityProviderOptions;\n }>;\n },\n ) {\n env.registerInit({\n deps: {\n catalog: catalogProcessingExtensionPoint,\n config: configServiceRef,\n database: databaseServiceRef,\n httpRouter: httpRouterServiceRef,\n logger: loggerServiceRef,\n scheduler: schedulerServiceRef,\n },\n async init({\n catalog,\n config,\n database,\n httpRouter,\n logger,\n scheduler,\n }) {\n const client = await database.getClient();\n\n const providers = new WrapperProviders({\n config,\n logger,\n client,\n scheduler,\n });\n\n for (const entry of options.providers) {\n const wrapped = providers.wrap(entry.provider, entry.options);\n catalog.addEntityProvider(wrapped);\n }\n\n httpRouter.use(await providers.adminRouter());\n },\n });\n },\n });\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n IncrementalEntityProvider,\n IncrementalEntityProviderOptions,\n PluginEnvironment,\n} from '../types';\nimport { CatalogBuilder as CoreCatalogBuilder } from '@backstage/plugin-catalog-backend';\nimport { Duration } from 'luxon';\nimport { Knex } from 'knex';\nimport { IncrementalIngestionEngine } from '../engine/IncrementalIngestionEngine';\nimport { applyDatabaseMigrations } from '../database/migrations';\nimport { IncrementalIngestionDatabaseManager } from '../database/IncrementalIngestionDatabaseManager';\nimport { createIncrementalProviderRouter } from '../router/routes';\nimport { Deferred } from '../util';\n\n/** @public */\nexport class IncrementalCatalogBuilder {\n /**\n * Creates the incremental catalog builder, which extends the regular catalog builder.\n * @param env - PluginEnvironment\n * @param builder - CatalogBuilder\n * @returns IncrementalCatalogBuilder\n */\n static async create(env: PluginEnvironment, builder: CoreCatalogBuilder) {\n const client = await env.database.getClient();\n const manager = new IncrementalIngestionDatabaseManager({ client });\n return new IncrementalCatalogBuilder(env, builder, client, manager);\n }\n\n private ready: Deferred<void>;\n\n private constructor(\n private env: PluginEnvironment,\n private builder: CoreCatalogBuilder,\n private client: Knex,\n private manager: IncrementalIngestionDatabaseManager,\n ) {\n this.ready = new Deferred<void>();\n }\n\n async build() {\n await applyDatabaseMigrations(this.client);\n this.ready.resolve();\n\n const routerLogger = this.env.logger.child({\n router: 'IncrementalProviderAdmin',\n });\n\n const incrementalAdminRouter = await createIncrementalProviderRouter(\n this.manager,\n routerLogger,\n );\n\n return { incrementalAdminRouter };\n }\n\n addIncrementalEntityProvider<TCursor, TContext>(\n provider: IncrementalEntityProvider<TCursor, TContext>,\n options: IncrementalEntityProviderOptions,\n ) {\n const { burstInterval, burstLength, restLength } = options;\n const { logger: catalogLogger, scheduler } = this.env;\n const ready = this.ready;\n\n const manager = this.manager;\n\n this.builder.addEntityProvider({\n getProviderName: provider.getProviderName.bind(provider),\n async connect(connection) {\n const logger = catalogLogger.child({\n entityProvider: provider.getProviderName(),\n });\n\n logger.info(`Connecting`);\n\n const engine = new IncrementalIngestionEngine({\n ...options,\n ready,\n manager,\n logger,\n provider,\n restLength,\n connection,\n });\n\n const frequency = Duration.isDuration(burstInterval)\n ? burstInterval\n : Duration.fromObject(burstInterval);\n const length = Duration.isDuration(burstLength)\n ? burstLength\n : Duration.fromObject(burstLength);\n\n await scheduler.scheduleTask({\n id: provider.getProviderName(),\n fn: engine.taskFn.bind(engine),\n frequency,\n timeout: length,\n });\n },\n });\n }\n}\n"],"names":["v4","stringifyEntityRef","resolvePackagePath","Duration","stringifyError","performance","Router","express","errorHandler","loggerToWinstonLogger","createBackendModule","catalogProcessingExtensionPoint","configServiceRef","databaseServiceRef","httpRouterServiceRef","loggerServiceRef","schedulerServiceRef"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/types.ts","../src/database/IncrementalIngestionDatabaseManager.ts","../src/database/tables.ts","../src/database/migrations.ts","../src/engine/IncrementalIngestionEngine.ts","../src/router/paths.ts","../src/router/routes.ts","../src/util.ts","../src/module/WrapperProviders.ts","../src/module/incrementalIngestionEntityProviderCatalogModule.ts","../src/service/IncrementalCatalogBuilder.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n PluginDatabaseManager,\n UrlReader,\n} from '@backstage/backend-common';\nimport type {\n PluginTaskScheduler,\n TaskFunction,\n} from '@backstage/backend-tasks';\nimport type { Config } from '@backstage/config';\nimport type {\n DeferredEntity,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-backend';\nimport type { PermissionEvaluator } from '@backstage/plugin-permission-common';\nimport type { DurationObjectUnits } from 'luxon';\nimport type { Logger } from 'winston';\nimport { IncrementalIngestionDatabaseManager } from './database/IncrementalIngestionDatabaseManager';\n\n/**\n * Entity annotation containing the incremental entity provider.\n *\n * @public\n */\nexport const INCREMENTAL_ENTITY_PROVIDER_ANNOTATION =\n 'backstage.io/incremental-provider-name';\n\n/**\n * Ingest entities into the catalog in bite-sized chunks.\n *\n * A Normal `EntityProvider` allows you to introduce entities into the\n * processing pipeline by calling an `applyMutation()` on the full set\n * of entities. However, this is not great when the number of entities\n * that you have to keep track of is extremely large because it\n * entails having all of them in memory at once. An\n * `IncrementalEntityProvider` by contrast allows you to provide\n * batches of entities in sequence so that you never need to have more\n * than a few hundred in memory at a time.\n *\n * @public\n */\nexport interface IncrementalEntityProvider<TCursor, TContext> {\n /**\n * This name must be unique between all of the entity providers\n * operating in the catalog.\n */\n getProviderName(): string;\n\n /**\n * Return a single page of entities from a specific point in the\n * ingestion.\n *\n * @param context - anything needed in order to fetch a single page.\n * @param cursor - a unique value identifying the page to ingest.\n * @returns The entities to be ingested, as well as the cursor of\n * the next page after this one.\n */\n next(\n context: TContext,\n cursor?: TCursor,\n ): Promise<EntityIteratorResult<TCursor>>;\n\n /**\n * Do any setup and teardown necessary in order to provide the\n * context for fetching pages. This should always invoke `burst` in\n * order to fetch the individual pages.\n *\n * @param burst - a function which performs a series of iterations\n */\n around(burst: (context: TContext) => Promise<void>): Promise<void>;\n}\n\n/**\n * Value returned by an {@link IncrementalEntityProvider} to provide a\n * single page of entities to ingest.\n *\n * @public\n */\nexport type EntityIteratorResult<T> =\n | {\n done: false;\n entities: DeferredEntity[];\n cursor: T;\n }\n | {\n done: true;\n entities?: DeferredEntity[];\n cursor?: T;\n };\n\n/** @public */\nexport interface IncrementalEntityProviderOptions {\n /**\n * Entities are ingested in bursts. This interval determines how\n * much time to wait in between each burst.\n */\n burstInterval: DurationObjectUnits;\n\n /**\n * Entities are ingested in bursts. This value determines how long\n * to keep ingesting within each burst.\n */\n burstLength: DurationObjectUnits;\n\n /**\n * After a successful ingestion, the incremental entity provider\n * will rest for this period of time before starting to ingest\n * again.\n */\n restLength: DurationObjectUnits;\n\n /**\n * In the event of an error during an ingestion burst, the backoff\n * determines how soon it will be retried. E.g.\n * `[{ minutes: 1}, { minutes: 5}, {minutes: 30 }, { hours: 3 }]`\n */\n backoff?: DurationObjectUnits[];\n}\n\n/** @public */\nexport type PluginEnvironment = {\n logger: Logger;\n database: PluginDatabaseManager;\n scheduler: PluginTaskScheduler;\n config: Config;\n reader: UrlReader;\n permissions: PermissionEvaluator;\n};\n\nexport interface IterationEngine {\n taskFn: TaskFunction;\n}\n\nexport interface IterationEngineOptions {\n logger: Logger;\n connection: EntityProviderConnection;\n manager: IncrementalIngestionDatabaseManager;\n provider: IncrementalEntityProvider<unknown, unknown>;\n restLength: DurationObjectUnits;\n ready: Promise<void>;\n backoff?: IncrementalEntityProviderOptions['backoff'];\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport type { DeferredEntity } from '@backstage/plugin-catalog-backend';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { Duration } from 'luxon';\nimport { v4 } from 'uuid';\nimport { INCREMENTAL_ENTITY_PROVIDER_ANNOTATION } from '../types';\nimport {\n IngestionRecord,\n IngestionRecordUpdate,\n IngestionUpsert,\n MarkRecord,\n MarkRecordInsert,\n} from './tables';\n\nexport class IncrementalIngestionDatabaseManager {\n private client: Knex;\n\n constructor(options: { client: Knex }) {\n this.client = options.client;\n }\n\n /**\n * Performs an update to the ingestion record with matching `id`.\n * @param options - IngestionRecordUpdate\n */\n async updateIngestionRecordById(options: IngestionRecordUpdate) {\n await this.client.transaction(async tx => {\n const { ingestionId, update } = options;\n await tx('ingestions').where('id', ingestionId).update(update);\n });\n }\n\n /**\n * Performs an update to the ingestion record with matching provider name. Will only update active records.\n * @param provider - string\n * @param update - Partial<IngestionUpsertIFace>\n */\n async updateIngestionRecordByProvider(\n provider: string,\n update: Partial<IngestionUpsert>,\n ) {\n await this.client.transaction(async tx => {\n await tx('ingestions')\n .where('provider_name', provider)\n .andWhere('completion_ticket', 'open')\n .update(update);\n });\n }\n\n /**\n * Performs an insert into the `ingestions` table with the supplied values.\n * @param record - IngestionUpsertIFace\n */\n async insertIngestionRecord(record: IngestionUpsert) {\n await this.client.transaction(async tx => {\n await tx('ingestions').insert(record);\n });\n }\n\n private async deleteMarkEntities(\n tx: Knex.Transaction,\n ids: { id: string }[],\n ) {\n const chunks: { id: string }[][] = [];\n for (let i = 0; i < ids.length; i += 100) {\n const chunk = ids.slice(i, i + 100);\n chunks.push(chunk);\n }\n\n let deleted = 0;\n\n for (const chunk of chunks) {\n const chunkDeleted = await tx('ingestion_mark_entities')\n .delete()\n .whereIn(\n 'id',\n chunk.map(entry => entry.id),\n );\n deleted += chunkDeleted;\n }\n\n return deleted;\n }\n\n /**\n * Finds the current ingestion record for the named provider.\n * @param provider - string\n * @returns IngestionRecord | undefined\n */\n async getCurrentIngestionRecord(provider: string) {\n return await this.client.transaction(async tx => {\n const record = await tx<IngestionRecord>('ingestions')\n .where('provider_name', provider)\n .andWhere('completion_ticket', 'open')\n .first();\n return record;\n });\n }\n\n /**\n * Removes all entries from `ingestion_marks_entities`, `ingestion_marks`, and `ingestions`\n * for prior ingestions that completed (i.e., have a `completion_ticket` value other than 'open').\n * @param provider - string\n * @returns A count of deletions for each record type.\n */\n async clearFinishedIngestions(provider: string) {\n return await this.client.transaction(async tx => {\n const markEntitiesDeleted = await tx('ingestion_mark_entities')\n .delete()\n .whereIn(\n 'ingestion_mark_id',\n tx('ingestion_marks')\n .select('id')\n .whereIn(\n 'ingestion_id',\n tx('ingestions')\n .select('id')\n .where('provider_name', provider)\n .andWhereNot('completion_ticket', 'open'),\n ),\n );\n\n const marksDeleted = await tx('ingestion_marks')\n .delete()\n .whereIn(\n 'ingestion_id',\n tx('ingestions')\n .select('id')\n .where('provider_name', provider)\n .andWhereNot('completion_ticket', 'open'),\n );\n\n const ingestionsDeleted = await tx('ingestions')\n .delete()\n .where('provider_name', provider)\n .andWhereNot('completion_ticket', 'open');\n\n return {\n deletions: {\n markEntitiesDeleted,\n marksDeleted,\n ingestionsDeleted,\n },\n };\n });\n }\n\n /**\n * Automatically cleans up duplicate ingestion records if they were accidentally created.\n * Any ingestion record where the `rest_completed_at` is null (meaning it is active) AND\n * the ingestionId is incorrect is a duplicate ingestion record.\n * @param ingestionId - string\n * @param provider - string\n */\n async clearDuplicateIngestions(ingestionId: string, provider: string) {\n await this.client.transaction(async tx => {\n const invalid = await tx<IngestionRecord>('ingestions')\n .where('provider_name', provider)\n .andWhere('rest_completed_at', null)\n .andWhereNot('id', ingestionId);\n\n if (invalid.length > 0) {\n await tx('ingestions').delete().whereIn('id', invalid);\n await tx('ingestion_mark_entities')\n .delete()\n .whereIn(\n 'ingestion_mark_id',\n tx('ingestion_marks').select('id').whereIn('ingestion_id', invalid),\n );\n await tx('ingestion_marks').delete().whereIn('ingestion_id', invalid);\n }\n });\n }\n\n /**\n * This method fully purges and resets all ingestion records for the named provider, and\n * leaves it in a paused state.\n * @param provider - string\n * @returns Counts of all deleted ingestion records\n */\n async purgeAndResetProvider(provider: string) {\n return await this.client.transaction(async tx => {\n const ingestionIDs: { id: string }[] = await tx('ingestions')\n .select('id')\n .where('provider_name', provider);\n\n const markIDs: { id: string }[] =\n ingestionIDs.length > 0\n ? await tx('ingestion_marks')\n .select('id')\n .whereIn(\n 'ingestion_id',\n ingestionIDs.map(entry => entry.id),\n )\n : [];\n\n const markEntityIDs: { id: string }[] =\n markIDs.length > 0\n ? await tx('ingestion_mark_entities')\n .select('id')\n .whereIn(\n 'ingestion_mark_id',\n markIDs.map(entry => entry.id),\n )\n : [];\n\n const markEntitiesDeleted = await this.deleteMarkEntities(\n tx,\n markEntityIDs,\n );\n\n const marksDeleted =\n markIDs.length > 0\n ? await tx('ingestion_marks')\n .delete()\n .whereIn(\n 'ingestion_id',\n ingestionIDs.map(entry => entry.id),\n )\n : 0;\n\n const ingestionsDeleted = await tx('ingestions')\n .delete()\n .where('provider_name', provider);\n\n const next_action_at = new Date();\n next_action_at.setTime(next_action_at.getTime() + 24 * 60 * 60 * 1000);\n\n await this.insertIngestionRecord({\n id: v4(),\n next_action: 'rest',\n provider_name: provider,\n next_action_at,\n ingestion_completed_at: new Date(),\n status: 'resting',\n completion_ticket: 'open',\n });\n\n return { provider, ingestionsDeleted, marksDeleted, markEntitiesDeleted };\n });\n }\n\n /**\n * Creates a new ingestion record.\n * @param provider - string\n * @returns A new ingestion record\n */\n async createProviderIngestionRecord(provider: string) {\n const ingestionId = v4();\n const nextAction = 'ingest';\n try {\n await this.insertIngestionRecord({\n id: ingestionId,\n next_action: nextAction,\n provider_name: provider,\n status: 'bursting',\n completion_ticket: 'open',\n });\n return { ingestionId, nextAction, attempts: 0, nextActionAt: Date.now() };\n } catch (_e) {\n // Creating the ingestion record failed. Return undefined.\n return undefined;\n }\n }\n\n /**\n * Computes which entities to remove, if any, at the end of a burst.\n * @param provider - string\n * @param ingestionId - string\n * @returns All entities to remove for this burst.\n */\n async computeRemoved(provider: string, ingestionId: string) {\n return await this.client.transaction(async tx => {\n const removed: { entity: string; ref: string }[] = await tx(\n 'final_entities',\n )\n .select(\n tx.ref('final_entity').as('entity'),\n tx.ref('refresh_state.entity_ref').as('ref'),\n )\n .join(\n 'refresh_state',\n 'refresh_state.entity_id',\n 'final_entities.entity_id',\n )\n .join('search', 'search.entity_id', 'final_entities.entity_id')\n .whereNotIn(\n 'entity_ref',\n tx('ingestion_marks')\n .join(\n 'ingestion_mark_entities',\n 'ingestion_marks.id',\n 'ingestion_mark_entities.ingestion_mark_id',\n )\n .select('ingestion_mark_entities.ref')\n .where('ingestion_marks.ingestion_id', ingestionId),\n )\n .andWhere(\n 'search.key',\n `metadata.annotations.${INCREMENTAL_ENTITY_PROVIDER_ANNOTATION}`,\n )\n .andWhere('search.value', provider);\n return removed.map(entity => {\n return { entity: JSON.parse(entity.entity) };\n });\n });\n }\n\n /**\n * Performs a lookup of all providers that have duplicate active ingestion records.\n * @returns An array of all duplicate active ingestions\n */\n async healthcheck() {\n return await this.client.transaction(async tx => {\n const records = await tx<{ id: string; provider_name: string }>(\n 'ingestions',\n )\n .distinct('id', 'provider_name')\n .where('rest_completed_at', null);\n return records;\n });\n }\n\n /**\n * Skips any wait time for the next action to run.\n * @param provider - string\n */\n async triggerNextProviderAction(provider: string) {\n await this.updateIngestionRecordByProvider(provider, {\n next_action_at: new Date(),\n });\n }\n\n /**\n * Purges the following tables:\n * * `ingestions`\n * * `ingestion_marks`\n * * `ingestion_mark_entities`\n *\n * This function leaves the ingestions table with all providers in a paused state.\n * @returns Results from cleaning up all ingestion tables.\n */\n async cleanupProviders() {\n const providers = await this.listProviders();\n\n const ingestionsDeleted = await this.purgeTable('ingestions');\n\n const next_action_at = new Date();\n next_action_at.setTime(next_action_at.getTime() + 24 * 60 * 60 * 1000);\n\n for (const provider of providers) {\n await this.insertIngestionRecord({\n id: v4(),\n next_action: 'rest',\n provider_name: provider,\n next_action_at,\n ingestion_completed_at: new Date(),\n status: 'resting',\n completion_ticket: 'open',\n });\n }\n\n const ingestionMarksDeleted = await this.purgeTable('ingestion_marks');\n const markEntitiesDeleted = await this.purgeTable(\n 'ingestion_mark_entities',\n );\n\n return { ingestionsDeleted, ingestionMarksDeleted, markEntitiesDeleted };\n }\n\n /**\n * Configures the current ingestion record to ingest a burst.\n * @param ingestionId - string\n */\n async setProviderIngesting(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: { next_action: 'ingest' },\n });\n }\n\n /**\n * Indicates the provider is currently ingesting a burst.\n * @param ingestionId - string\n */\n async setProviderBursting(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: { status: 'bursting' },\n });\n }\n\n /**\n * Finalizes the current ingestion record to indicate that the post-ingestion rest period is complete.\n * @param ingestionId - string\n */\n async setProviderComplete(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: {\n next_action: 'nothing (done)',\n rest_completed_at: new Date(),\n status: 'complete',\n completion_ticket: v4(),\n },\n });\n }\n\n /**\n * Marks ingestion as complete and starts the post-ingestion rest cycle.\n * @param ingestionId - string\n * @param restLength - Duration\n */\n async setProviderResting(ingestionId: string, restLength: Duration) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: {\n next_action: 'rest',\n next_action_at: new Date(Date.now() + restLength.as('milliseconds')),\n ingestion_completed_at: new Date(),\n status: 'resting',\n },\n });\n }\n\n /**\n * Marks ingestion as paused after a burst completes.\n * @param ingestionId - string\n */\n async setProviderInterstitial(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: { attempts: 0, status: 'interstitial' },\n });\n }\n\n /**\n * Starts the cancel process for the current ingestion.\n * @param ingestionId - string\n * @param message - string (optional)\n */\n async setProviderCanceling(ingestionId: string, message?: string) {\n const update: Partial<IngestionUpsert> = {\n next_action: 'cancel',\n last_error: message ? message : undefined,\n next_action_at: new Date(),\n status: 'canceling',\n };\n await this.updateIngestionRecordById({ ingestionId, update });\n }\n\n /**\n * Completes the cancel process and triggers a new ingestion.\n * @param ingestionId - string\n */\n async setProviderCanceled(ingestionId: string) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: {\n next_action: 'nothing (canceled)',\n rest_completed_at: new Date(),\n status: 'complete',\n completion_ticket: v4(),\n },\n });\n }\n\n /**\n * Configures the current ingestion to wait and retry, due to a data source error.\n * @param ingestionId - string\n * @param attempts - number\n * @param error - Error\n * @param backoffLength - number\n */\n async setProviderBackoff(\n ingestionId: string,\n attempts: number,\n error: Error,\n backoffLength: number,\n ) {\n await this.updateIngestionRecordById({\n ingestionId,\n update: {\n next_action: 'backoff',\n attempts: attempts + 1,\n last_error: String(error),\n next_action_at: new Date(Date.now() + backoffLength),\n status: 'backing off',\n },\n });\n }\n\n /**\n * Returns the last record from `ingestion_marks` for the supplied ingestionId.\n * @param ingestionId - string\n * @returns MarkRecord | undefined\n */\n async getLastMark(ingestionId: string) {\n return await this.client.transaction(async tx => {\n const mark = await tx<MarkRecord>('ingestion_marks')\n .where('ingestion_id', ingestionId)\n .orderBy('sequence', 'desc')\n .first();\n return mark;\n });\n }\n\n async getAllMarks(ingestionId: string) {\n return await this.client.transaction(async tx => {\n const marks = await tx<MarkRecord>('ingestion_marks')\n .where('ingestion_id', ingestionId)\n .orderBy('sequence', 'desc');\n return marks;\n });\n }\n\n /**\n * Performs an insert into the `ingestion_marks` table with the supplied values.\n * @param options - MarkRecordInsert\n */\n async createMark(options: MarkRecordInsert) {\n const { record } = options;\n await this.client.transaction(async tx => {\n await tx('ingestion_marks').insert(record);\n });\n }\n /**\n * Performs an upsert to the `ingestion_mark_entities` table for all deferred entities.\n * @param markId - string\n * @param entities - DeferredEntity[]\n */\n async createMarkEntities(markId: string, entities: DeferredEntity[]) {\n await this.client.transaction(async tx => {\n await tx('ingestion_mark_entities').insert(\n entities.map(entity => ({\n id: v4(),\n ingestion_mark_id: markId,\n ref: stringifyEntityRef(entity.entity),\n })),\n );\n });\n }\n\n /**\n * Deletes the entire content of a table, and returns the number of records deleted.\n * @param table - string\n * @returns number\n */\n async purgeTable(table: string) {\n return await this.client.transaction(async tx => {\n return await tx(table).delete();\n });\n }\n\n /**\n * Returns a list of all providers.\n * @returns string[]\n */\n async listProviders() {\n return await this.client.transaction(async tx => {\n const providers = await tx<{ provider_name: string }>(\n 'ingestions',\n ).distinct('provider_name');\n return providers.map(entry => entry.provider_name);\n });\n }\n\n async updateByName(provider: string, update: Partial<IngestionUpsert>) {\n await this.updateIngestionRecordByProvider(provider, update);\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport const DB_MIGRATIONS_TABLE = 'incremental_ingestion__knex_migrations';\n\n/**\n * The shape of data inserted into or updated in the `ingestions` table.\n */\nexport interface IngestionUpsert {\n /**\n * The ingestion record id.\n */\n id?: string;\n /**\n * The next action the incremental entity provider will take.\n */\n next_action:\n | 'rest'\n | 'ingest'\n | 'backoff'\n | 'cancel'\n | 'nothing (done)'\n | 'nothing (canceled)';\n /**\n * Current status of the incremental entity provider.\n */\n status:\n | 'complete'\n | 'bursting'\n | 'resting'\n | 'canceling'\n | 'interstitial'\n | 'backing off';\n /**\n * The name of the incremental entity provider being updated.\n */\n provider_name: string;\n /**\n * Date/time stamp for when the next action will trigger.\n */\n next_action_at?: Date;\n /**\n * A record of the last error generated by the incremental entity provider.\n */\n last_error?: string | null;\n /**\n * The number of attempts the provider has attempted during the current cycle.\n */\n attempts?: number;\n /**\n * Date/time stamp for the completion of ingestion.\n */\n ingestion_completed_at?: Date | string | null;\n /**\n * Date/time stamp for the end of the rest cycle before the next ingestion.\n */\n rest_completed_at?: Date | string | null;\n /**\n * A record of the finalized status of the ingestion record. Values are either 'open' or a uuid.\n */\n completion_ticket: string;\n}\n\n/**\n * This interface is for updating an existing ingestion record.\n */\nexport interface IngestionRecordUpdate {\n ingestionId: string;\n update: Partial<IngestionUpsert>;\n}\n\n/**\n * The expected response from the `ingestion_marks` table.\n */\nexport interface MarkRecord {\n id: string;\n sequence: number;\n ingestion_id: string;\n cursor: string;\n created_at: string;\n}\n\n/**\n * The expected response from the `ingestions` table.\n */\nexport interface IngestionRecord extends IngestionUpsert {\n id: string;\n next_action_at: Date;\n /**\n * The date/time the ingestion record was created.\n */\n created_at: string;\n}\n\n/**\n * This interface supplies all the values for adding an ingestion mark.\n */\nexport interface MarkRecordInsert {\n record: {\n id: string;\n ingestion_id: string;\n cursor: unknown;\n sequence: number;\n };\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { resolvePackagePath } from '@backstage/backend-common';\nimport { Knex } from 'knex';\nimport { DB_MIGRATIONS_TABLE } from './tables';\n\nexport async function applyDatabaseMigrations(knex: Knex): Promise<void> {\n const migrationsDir = resolvePackagePath(\n '@backstage/plugin-catalog-backend-module-incremental-ingestion',\n 'migrations',\n );\n\n await knex.migrate.latest({\n directory: migrationsDir,\n tableName: DB_MIGRATIONS_TABLE,\n });\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { DeferredEntity } from '@backstage/plugin-catalog-backend';\nimport {\n INCREMENTAL_ENTITY_PROVIDER_ANNOTATION,\n IterationEngine,\n IterationEngineOptions,\n} from '../types';\nimport { IncrementalIngestionDatabaseManager } from '../database/IncrementalIngestionDatabaseManager';\nimport { performance } from 'perf_hooks';\nimport { Duration, DurationObjectUnits } from 'luxon';\nimport { v4 } from 'uuid';\nimport { stringifyError } from '@backstage/errors';\n\nexport class IncrementalIngestionEngine implements IterationEngine {\n private readonly restLength: Duration;\n private readonly backoff: DurationObjectUnits[];\n\n private manager: IncrementalIngestionDatabaseManager;\n\n constructor(private options: IterationEngineOptions) {\n this.manager = options.manager;\n this.restLength = Duration.fromObject(options.restLength);\n this.backoff = options.backoff ?? [\n { minutes: 1 },\n { minutes: 5 },\n { minutes: 30 },\n { hours: 3 },\n ];\n }\n\n async taskFn(signal: AbortSignal) {\n try {\n this.options.logger.debug('Begin tick');\n await this.handleNextAction(signal);\n } catch (error) {\n this.options.logger.error(`${error}`);\n throw error;\n } finally {\n this.options.logger.debug('End tick');\n }\n }\n\n async handleNextAction(signal: AbortSignal) {\n await this.options.ready;\n\n const result = await this.getCurrentAction();\n if (result) {\n const { ingestionId, nextActionAt, nextAction, attempts } = result;\n\n switch (nextAction) {\n case 'rest':\n if (Date.now() > nextActionAt) {\n await this.manager.clearFinishedIngestions(\n this.options.provider.getProviderName(),\n );\n this.options.logger.info(\n `incremental-engine: Ingestion ${ingestionId} rest period complete. Ingestion will start again`,\n );\n\n await this.manager.setProviderComplete(ingestionId);\n } else {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' rest period continuing`,\n );\n }\n break;\n case 'ingest':\n try {\n await this.manager.setProviderBursting(ingestionId);\n const done = await this.ingestOneBurst(ingestionId, signal);\n if (done) {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' complete, transitioning to rest period of ${this.restLength.toHuman()}`,\n );\n await this.manager.setProviderResting(\n ingestionId,\n this.restLength,\n );\n } else {\n await this.manager.setProviderInterstitial(ingestionId);\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' continuing`,\n );\n }\n } catch (error) {\n if (\n (error as Error).message &&\n (error as Error).message === 'CANCEL'\n ) {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' canceled`,\n );\n await this.manager.setProviderCanceling(\n ingestionId,\n (error as Error).message,\n );\n } else {\n const currentBackoff = Duration.fromObject(\n this.backoff[Math.min(this.backoff.length - 1, attempts)],\n );\n\n const backoffLength = currentBackoff.as('milliseconds');\n this.options.logger.error(error);\n\n const truncatedError = stringifyError(error).substring(0, 700);\n this.options.logger.error(\n `incremental-engine: Ingestion '${ingestionId}' threw an error during ingestion burst. Ingestion will backoff for ${currentBackoff.toHuman()} (${truncatedError})`,\n );\n\n await this.manager.setProviderBackoff(\n ingestionId,\n attempts,\n error as Error,\n backoffLength,\n );\n }\n }\n break;\n case 'backoff':\n if (Date.now() > nextActionAt) {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' backoff complete, will attempt to resume`,\n );\n await this.manager.setProviderIngesting(ingestionId);\n } else {\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' backoff continuing`,\n );\n }\n break;\n case 'cancel':\n this.options.logger.info(\n `incremental-engine: Ingestion '${ingestionId}' canceling, will restart`,\n );\n await this.manager.setProviderCanceled(ingestionId);\n break;\n default:\n this.options.logger.error(\n `incremental-engine: Ingestion '${ingestionId}' received unknown action '${nextAction}'`,\n );\n }\n } else {\n this.options.logger.error(\n `incremental-engine: Engine tried to create duplicate ingestion record for provider '${this.options.provider.getProviderName()}'.`,\n );\n }\n }\n\n async getCurrentAction() {\n const providerName = this.options.provider.getProviderName();\n const record = await this.manager.getCurrentIngestionRecord(providerName);\n if (record) {\n this.options.logger.info(\n `incremental-engine: Ingestion record found: '${record.id}'`,\n );\n return {\n ingestionId: record.id,\n nextAction: record.next_action as 'rest' | 'ingest' | 'backoff',\n attempts: record.attempts as number,\n nextActionAt: record.next_action_at.valueOf() as number,\n };\n }\n const result = await this.manager.createProviderIngestionRecord(\n providerName,\n );\n if (result) {\n this.options.logger.info(\n `incremental-engine: Ingestion record created: '${result.ingestionId}'`,\n );\n }\n return result;\n }\n\n async ingestOneBurst(id: string, signal: AbortSignal) {\n const lastMark = await this.manager.getLastMark(id);\n\n const cursor = lastMark ? lastMark.cursor : undefined;\n let sequence = lastMark ? lastMark.sequence + 1 : 0;\n\n const start = performance.now();\n let count = 0;\n let done = false;\n this.options.logger.info(\n `incremental-engine: Ingestion '${id}' burst initiated`,\n );\n\n await this.options.provider.around(async (context: unknown) => {\n let next = await this.options.provider.next(context, cursor);\n count++;\n for (;;) {\n done = next.done;\n await this.mark({\n id,\n sequence,\n entities: next?.entities,\n done: next.done,\n cursor: next?.cursor,\n });\n if (signal.aborted || next.done) {\n break;\n } else {\n next = await this.options.provider.next(context, next.cursor);\n count++;\n sequence++;\n }\n }\n });\n\n this.options.logger.info(\n `incremental-engine: Ingestion '${id}' burst complete. (${count} batches in ${Math.round(\n performance.now() - start,\n )}ms).`,\n );\n return done;\n }\n\n async mark(options: {\n id: string;\n sequence: number;\n entities?: DeferredEntity[];\n done: boolean;\n cursor?: unknown;\n }) {\n const { id, sequence, entities, done, cursor } = options;\n this.options.logger.debug(\n `incremental-engine: Ingestion '${id}': MARK ${\n entities ? entities.length : 0\n } entities, cursor: ${\n cursor ? JSON.stringify(cursor) : 'none'\n }, done: ${done}`,\n );\n const markId = v4();\n\n await this.manager.createMark({\n record: {\n id: markId,\n ingestion_id: id,\n cursor,\n sequence,\n },\n });\n\n if (entities && entities.length > 0) {\n await this.manager.createMarkEntities(markId, entities);\n }\n\n const added =\n entities?.map(deferred => ({\n ...deferred,\n entity: {\n ...deferred.entity,\n metadata: {\n ...deferred.entity.metadata,\n annotations: {\n ...deferred.entity.metadata.annotations,\n [INCREMENTAL_ENTITY_PROVIDER_ANNOTATION]:\n this.options.provider.getProviderName(),\n },\n },\n },\n })) ?? [];\n\n const removed: DeferredEntity[] = [];\n\n if (done) {\n removed.push(\n ...(await this.manager.computeRemoved(\n this.options.provider.getProviderName(),\n id,\n )),\n );\n }\n\n await this.options.connection.applyMutation({\n type: 'delta',\n added,\n removed,\n });\n }\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport const PROVIDER_CLEANUP = '/incremental/cleanup';\nexport const PROVIDER_HEALTH = '/incremental/health';\nexport const PROVIDER_BASE_PATH = '/incremental/providers/:provider';\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { errorHandler } from '@backstage/backend-common';\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { Logger } from 'winston';\nimport { IncrementalIngestionDatabaseManager } from '../database/IncrementalIngestionDatabaseManager';\nimport { PROVIDER_BASE_PATH, PROVIDER_CLEANUP, PROVIDER_HEALTH } from './paths';\n\nexport const createIncrementalProviderRouter = async (\n manager: IncrementalIngestionDatabaseManager,\n logger: Logger,\n) => {\n const router = Router();\n router.use(express.json());\n\n // Get the overall health of all incremental providers\n router.get(PROVIDER_HEALTH, async (_, res) => {\n const records = await manager.healthcheck();\n const providers = records.map(record => record.provider_name);\n const duplicates = [\n ...new Set(providers.filter((e, i, a) => a.indexOf(e) !== i)),\n ];\n\n if (duplicates.length > 0) {\n res.json({ healthy: false, duplicateIngestions: duplicates });\n } else {\n res.json({ healthy: true });\n }\n });\n\n // Clean up and pause all providers\n router.post(PROVIDER_CLEANUP, async (_, res) => {\n const result = await manager.cleanupProviders();\n res.json(result);\n });\n\n // Get basic status of the provider\n router.get(PROVIDER_BASE_PATH, async (req, res) => {\n const { provider } = req.params;\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n res.json({\n success: true,\n status: {\n current_action: record.status,\n next_action_at: new Date(record.next_action_at),\n },\n last_error: record.last_error,\n });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n res.json({\n success: true,\n status: {\n current_action: 'rest complete, waiting to start',\n },\n });\n } else {\n logger.error(\n `${provider} - No ingestion record found in the database!`,\n );\n res.status(404).json({\n success: false,\n status: {},\n last_error: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n // Trigger the provider's next action\n router.post(`${PROVIDER_BASE_PATH}/trigger`, async (req, res) => {\n const { provider } = req.params;\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n await manager.triggerNextProviderAction(provider);\n res.json({\n success: true,\n message: `${provider}: Next action triggered.`,\n });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n logger.debug(`${provider} - Ingestion record found`);\n res.json({\n success: true,\n message: 'Unable to trigger next action (provider is restarting)',\n });\n } else {\n res.status(404).json({\n success: false,\n message: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n // Start a brand-new ingestion cycle for the provider.\n // (Cancel's the current run if active, or marks it complete if resting)\n router.post(`${PROVIDER_BASE_PATH}/start`, async (req, res) => {\n const { provider } = req.params;\n\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n const ingestionId = record.id;\n if (record.status === 'resting') {\n await manager.setProviderComplete(ingestionId);\n } else {\n await manager.setProviderCanceling(ingestionId);\n }\n res.json({\n success: true,\n message: `${provider}: Next cycle triggered.`,\n });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n logger.debug(`${provider} - Ingestion record found`);\n res.json({\n success: true,\n message: 'Provider is already restarting',\n });\n } else {\n res.status(404).json({\n success: false,\n message: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n // Stop the provider and pause it for 24 hours\n router.post(`${PROVIDER_BASE_PATH}/cancel`, async (req, res) => {\n const { provider } = req.params;\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n const next_action_at = new Date();\n next_action_at.setTime(next_action_at.getTime() + 24 * 60 * 60 * 1000);\n await manager.updateByName(provider, {\n next_action: 'nothing (done)',\n ingestion_completed_at: new Date(),\n next_action_at,\n status: 'resting',\n });\n res.json({\n success: true,\n message: `${provider}: Current ingestion canceled.`,\n });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n logger.debug(`${provider} - Ingestion record found`);\n res.json({\n success: true,\n message: 'Provider is currently restarting, please wait.',\n });\n } else {\n res.status(404).json({\n success: false,\n message: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n // Wipe out all ingestion records for the provider and pause for 24 hours\n router.delete(PROVIDER_BASE_PATH, async (req, res) => {\n const { provider } = req.params;\n const result = await manager.purgeAndResetProvider(provider);\n res.json(result);\n });\n\n // Get the ingestion marks for the current cycle\n router.get(`${PROVIDER_BASE_PATH}/marks`, async (req, res) => {\n const { provider } = req.params;\n const record = await manager.getCurrentIngestionRecord(provider);\n if (record) {\n const id = record.id;\n const records = await manager.getAllMarks(id);\n res.json({ success: true, records });\n } else {\n const providers: string[] = await manager.listProviders();\n if (providers.includes(provider)) {\n logger.debug(`${provider} - Ingestion record found`);\n res.json({\n success: true,\n message: 'No records yet (provider is restarting)',\n });\n } else {\n logger.error(\n `${provider} - No ingestion record found in the database!`,\n );\n res.status(404).json({\n success: false,\n status: {},\n last_error: `Provider '${provider}' not found`,\n });\n }\n }\n });\n\n router.delete(`${PROVIDER_BASE_PATH}/marks`, async (req, res) => {\n const { provider } = req.params;\n const deletions = await manager.clearFinishedIngestions(provider);\n\n res.json({\n success: true,\n message: `Expired marks for provider '${provider}' removed.`,\n deletions,\n });\n });\n\n router.use(errorHandler());\n\n return router;\n};\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport class Deferred<T> implements Promise<T> {\n #resolve?: (value: T) => void;\n #reject?: (error: Error) => void;\n\n public get resolve() {\n return this.#resolve!;\n }\n public get reject() {\n return this.#reject!;\n }\n\n public then: Promise<T>['then'];\n public catch: Promise<T>['catch'];\n public finally: Promise<T>['finally'];\n\n public constructor() {\n const promise = new Promise<T>((resolve, reject) => {\n this.#resolve = resolve;\n this.#reject = reject;\n });\n\n this.then = promise.then.bind(promise);\n this.catch = promise.catch.bind(promise);\n this.finally = promise.finally.bind(promise);\n }\n\n [Symbol.toStringTag]: 'Deferred' = 'Deferred';\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Logger, loggerToWinstonLogger } from '@backstage/backend-plugin-api';\nimport { PluginTaskScheduler } from '@backstage/backend-tasks';\nimport { Config } from '@backstage/config';\nimport { stringifyError } from '@backstage/errors';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport express from 'express';\nimport { Knex } from 'knex';\nimport { Duration } from 'luxon';\nimport { IncrementalIngestionDatabaseManager } from '../database/IncrementalIngestionDatabaseManager';\nimport { applyDatabaseMigrations } from '../database/migrations';\nimport { IncrementalIngestionEngine } from '../engine/IncrementalIngestionEngine';\nimport { createIncrementalProviderRouter } from '../router/routes';\nimport {\n IncrementalEntityProvider,\n IncrementalEntityProviderOptions,\n} from '../types';\nimport { Deferred } from '../util';\n\n/**\n * Helps in the creation of the catalog entity providers that wrap the\n * incremental ones.\n */\nexport class WrapperProviders {\n private migrate: Promise<void> | undefined;\n private numberOfProvidersToConnect = 0;\n private readonly readySignal = new Deferred<void>();\n\n constructor(\n private readonly options: {\n config: Config;\n logger: Logger;\n client: Knex;\n scheduler: PluginTaskScheduler;\n applyDatabaseMigrations?: typeof applyDatabaseMigrations;\n },\n ) {}\n\n wrap(\n provider: IncrementalEntityProvider<unknown, unknown>,\n options: IncrementalEntityProviderOptions,\n ): EntityProvider {\n this.numberOfProvidersToConnect += 1;\n return {\n getProviderName: () => provider.getProviderName(),\n connect: async connection => {\n await this.startProvider(provider, options, connection);\n this.numberOfProvidersToConnect -= 1;\n if (this.numberOfProvidersToConnect === 0) {\n this.readySignal.resolve();\n }\n },\n };\n }\n\n async adminRouter(): Promise<express.Router> {\n return createIncrementalProviderRouter(\n new IncrementalIngestionDatabaseManager({ client: this.options.client }),\n loggerToWinstonLogger(this.options.logger),\n );\n }\n\n private async startProvider(\n provider: IncrementalEntityProvider<unknown, unknown>,\n providerOptions: IncrementalEntityProviderOptions,\n connection: EntityProviderConnection,\n ) {\n const logger = loggerToWinstonLogger(\n this.options.logger.child({\n entityProvider: provider.getProviderName(),\n }),\n );\n\n try {\n if (!this.migrate) {\n this.migrate = Promise.resolve().then(async () => {\n const apply =\n this.options.applyDatabaseMigrations ?? applyDatabaseMigrations;\n await apply(this.options.client);\n });\n }\n\n await this.migrate;\n\n const { burstInterval, burstLength, restLength } = providerOptions;\n\n logger.info(`Connecting`);\n\n const manager = new IncrementalIngestionDatabaseManager({\n client: this.options.client,\n });\n const engine = new IncrementalIngestionEngine({\n ...providerOptions,\n ready: this.readySignal,\n manager,\n logger,\n provider,\n restLength,\n connection,\n });\n\n const frequency = Duration.isDuration(burstInterval)\n ? burstInterval\n : Duration.fromObject(burstInterval);\n const length = Duration.isDuration(burstLength)\n ? burstLength\n : Duration.fromObject(burstLength);\n\n await this.options.scheduler.scheduleTask({\n id: provider.getProviderName(),\n fn: engine.taskFn.bind(engine),\n frequency,\n timeout: length,\n });\n } catch (error) {\n logger.warn(\n `Failed to initialize incremental ingestion provider ${provider.getProviderName()}, ${stringifyError(\n error,\n )}`,\n );\n throw error;\n }\n }\n}\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configServiceRef,\n createBackendModule,\n databaseServiceRef,\n httpRouterServiceRef,\n loggerServiceRef,\n schedulerServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';\nimport {\n IncrementalEntityProvider,\n IncrementalEntityProviderOptions,\n} from '../types';\nimport { WrapperProviders } from './WrapperProviders';\n\n/**\n * Registers the incremental entity provider with the catalog processing extension point.\n *\n * @alpha\n */\nexport const incrementalIngestionEntityProviderCatalogModule =\n createBackendModule({\n pluginId: 'catalog',\n moduleId: 'incrementalIngestionEntityProvider',\n register(\n env,\n options: {\n providers: Array<{\n provider: IncrementalEntityProvider<unknown, unknown>;\n options: IncrementalEntityProviderOptions;\n }>;\n },\n ) {\n env.registerInit({\n deps: {\n catalog: catalogProcessingExtensionPoint,\n config: configServiceRef,\n database: databaseServiceRef,\n httpRouter: httpRouterServiceRef,\n logger: loggerServiceRef,\n scheduler: schedulerServiceRef,\n },\n async init({\n catalog,\n config,\n database,\n httpRouter,\n logger,\n scheduler,\n }) {\n const client = await database.getClient();\n\n const providers = new WrapperProviders({\n config,\n logger,\n client,\n scheduler,\n });\n\n for (const entry of options.providers) {\n const wrapped = providers.wrap(entry.provider, entry.options);\n catalog.addEntityProvider(wrapped);\n }\n\n httpRouter.use(await providers.adminRouter());\n },\n });\n },\n });\n","/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n IncrementalEntityProvider,\n IncrementalEntityProviderOptions,\n PluginEnvironment,\n} from '../types';\nimport { CatalogBuilder as CoreCatalogBuilder } from '@backstage/plugin-catalog-backend';\nimport { Duration } from 'luxon';\nimport { Knex } from 'knex';\nimport { IncrementalIngestionEngine } from '../engine/IncrementalIngestionEngine';\nimport { applyDatabaseMigrations } from '../database/migrations';\nimport { IncrementalIngestionDatabaseManager } from '../database/IncrementalIngestionDatabaseManager';\nimport { createIncrementalProviderRouter } from '../router/routes';\nimport { Deferred } from '../util';\n\n/** @public */\nexport class IncrementalCatalogBuilder {\n /**\n * Creates the incremental catalog builder, which extends the regular catalog builder.\n * @param env - PluginEnvironment\n * @param builder - CatalogBuilder\n * @returns IncrementalCatalogBuilder\n */\n static async create(env: PluginEnvironment, builder: CoreCatalogBuilder) {\n const client = await env.database.getClient();\n const manager = new IncrementalIngestionDatabaseManager({ client });\n return new IncrementalCatalogBuilder(env, builder, client, manager);\n }\n\n private ready: Deferred<void>;\n\n private constructor(\n private env: PluginEnvironment,\n private builder: CoreCatalogBuilder,\n private client: Knex,\n private manager: IncrementalIngestionDatabaseManager,\n ) {\n this.ready = new Deferred<void>();\n }\n\n async build() {\n await applyDatabaseMigrations(this.client);\n this.ready.resolve();\n\n const routerLogger = this.env.logger.child({\n router: 'IncrementalProviderAdmin',\n });\n\n const incrementalAdminRouter = await createIncrementalProviderRouter(\n this.manager,\n routerLogger,\n );\n\n return { incrementalAdminRouter };\n }\n\n addIncrementalEntityProvider<TCursor, TContext>(\n provider: IncrementalEntityProvider<TCursor, TContext>,\n options: IncrementalEntityProviderOptions,\n ) {\n const { burstInterval, burstLength, restLength } = options;\n const { logger: catalogLogger, scheduler } = this.env;\n const ready = this.ready;\n\n const manager = this.manager;\n\n this.builder.addEntityProvider({\n getProviderName: provider.getProviderName.bind(provider),\n async connect(connection) {\n const logger = catalogLogger.child({\n entityProvider: provider.getProviderName(),\n });\n\n logger.info(`Connecting`);\n\n const engine = new IncrementalIngestionEngine({\n ...options,\n ready,\n manager,\n logger,\n provider,\n restLength,\n connection,\n });\n\n const frequency = Duration.isDuration(burstInterval)\n ? burstInterval\n : Duration.fromObject(burstInterval);\n const length = Duration.isDuration(burstLength)\n ? burstLength\n : Duration.fromObject(burstLength);\n\n await scheduler.scheduleTask({\n id: provider.getProviderName(),\n fn: engine.taskFn.bind(engine),\n frequency,\n timeout: length,\n });\n },\n });\n }\n}\n"],"names":["v4","stringifyEntityRef","resolvePackagePath","Duration","stringifyError","performance","Router","express","errorHandler","loggerToWinstonLogger","createBackendModule","catalogProcessingExtensionPoint","configServiceRef","databaseServiceRef","httpRouterServiceRef","loggerServiceRef","schedulerServiceRef"],"mappings":";;;;;;;;;;;;;;;;;;;;AAuCO,MAAM,sCACX,GAAA;;ACVK,MAAM,mCAAoC,CAAA;AAAA,EAG/C,YAAY,OAA2B,EAAA;AACrC,IAAA,IAAA,CAAK,SAAS,OAAQ,CAAA,MAAA,CAAA;AAAA,GACxB;AAAA,EAMA,MAAM,0BAA0B,OAAgC,EAAA;AAC9D,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,WAAY,CAAA,OAAM,EAAM,KAAA;AACxC,MAAM,MAAA,EAAE,WAAa,EAAA,MAAA,EAAW,GAAA,OAAA,CAAA;AAChC,MAAM,MAAA,EAAA,CAAG,YAAY,CAAE,CAAA,KAAA,CAAM,MAAM,WAAW,CAAA,CAAE,OAAO,MAAM,CAAA,CAAA;AAAA,KAC9D,CAAA,CAAA;AAAA,GACH;AAAA,EAOA,MAAM,+BACJ,CAAA,QAAA,EACA,MACA,EAAA;AACA,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,WAAY,CAAA,OAAM,EAAM,KAAA;AACxC,MAAA,MAAM,EAAG,CAAA,YAAY,CAClB,CAAA,KAAA,CAAM,eAAiB,EAAA,QAAQ,CAC/B,CAAA,QAAA,CAAS,mBAAqB,EAAA,MAAM,CACpC,CAAA,MAAA,CAAO,MAAM,CAAA,CAAA;AAAA,KACjB,CAAA,CAAA;AAAA,GACH;AAAA,EAMA,MAAM,sBAAsB,MAAyB,EAAA;AACnD,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,WAAY,CAAA,OAAM,EAAM,KAAA;AACxC,MAAA,MAAM,EAAG,CAAA,YAAY,CAAE,CAAA,MAAA,CAAO,MAAM,CAAA,CAAA;AAAA,KACrC,CAAA,CAAA;AAAA,GACH;AAAA,EAEA,MAAc,kBACZ,CAAA,EAAA,EACA,GACA,EAAA;AACA,IAAA,MAAM,SAA6B,EAAC,CAAA;AACpC,IAAA,KAAA,IAAS,IAAI,CAAG,EAAA,CAAA,GAAI,GAAI,CAAA,MAAA,EAAQ,KAAK,GAAK,EAAA;AACxC,MAAA,MAAM,KAAQ,GAAA,GAAA,CAAI,KAAM,CAAA,CAAA,EAAG,IAAI,GAAG,CAAA,CAAA;AAClC,MAAA,MAAA,CAAO,KAAK,KAAK,CAAA,CAAA;AAAA,KACnB;AAEA,IAAA,IAAI,OAAU,GAAA,CAAA,CAAA;AAEd,IAAA,KAAA,MAAW,SAAS,MAAQ,EAAA;AAC1B,MAAA,MAAM,eAAe,MAAM,EAAA,CAAG,yBAAyB,CAAA,CACpD,QACA,CAAA,OAAA;AAAA,QACC,IAAA;AAAA,QACA,KAAM,CAAA,GAAA,CAAI,CAAS,KAAA,KAAA,KAAA,CAAM,EAAE,CAAA;AAAA,OAC7B,CAAA;AACF,MAAW,OAAA,IAAA,YAAA,CAAA;AAAA,KACb;AAEA,IAAO,OAAA,OAAA,CAAA;AAAA,GACT;AAAA,EAOA,MAAM,0BAA0B,QAAkB,EAAA;AAChD,IAAA,OAAO,MAAM,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,OAAM,EAAM,KAAA;AAC/C,MAAA,MAAM,MAAS,GAAA,MAAM,EAAoB,CAAA,YAAY,CAClD,CAAA,KAAA,CAAM,eAAiB,EAAA,QAAQ,CAC/B,CAAA,QAAA,CAAS,mBAAqB,EAAA,MAAM,EACpC,KAAM,EAAA,CAAA;AACT,MAAO,OAAA,MAAA,CAAA;AAAA,KACR,CAAA,CAAA;AAAA,GACH;AAAA,EAQA,MAAM,wBAAwB,QAAkB,EAAA;AAC9C,IAAA,OAAO,MAAM,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,OAAM,EAAM,KAAA;AAC/C,MAAA,MAAM,sBAAsB,MAAM,EAAA,CAAG,yBAAyB,CAAA,CAC3D,QACA,CAAA,OAAA;AAAA,QACC,mBAAA;AAAA,QACA,EAAG,CAAA,iBAAiB,CACjB,CAAA,MAAA,CAAO,IAAI,CACX,CAAA,OAAA;AAAA,UACC,cAAA;AAAA,UACA,EAAG,CAAA,YAAY,CACZ,CAAA,MAAA,CAAO,IAAI,CAAA,CACX,KAAM,CAAA,eAAA,EAAiB,QAAQ,CAAA,CAC/B,WAAY,CAAA,mBAAA,EAAqB,MAAM,CAAA;AAAA,SAC5C;AAAA,OACJ,CAAA;AAEF,MAAA,MAAM,eAAe,MAAM,EAAA,CAAG,iBAAiB,CAAA,CAC5C,QACA,CAAA,OAAA;AAAA,QACC,cAAA;AAAA,QACA,EAAG,CAAA,YAAY,CACZ,CAAA,MAAA,CAAO,IAAI,CAAA,CACX,KAAM,CAAA,eAAA,EAAiB,QAAQ,CAAA,CAC/B,WAAY,CAAA,mBAAA,EAAqB,MAAM,CAAA;AAAA,OAC5C,CAAA;AAEF,MAAA,MAAM,iBAAoB,GAAA,MAAM,EAAG,CAAA,YAAY,CAC5C,CAAA,MAAA,EACA,CAAA,KAAA,CAAM,eAAiB,EAAA,QAAQ,CAC/B,CAAA,WAAA,CAAY,qBAAqB,MAAM,CAAA,CAAA;AAE1C,MAAO,OAAA;AAAA,QACL,SAAW,EAAA;AAAA,UACT,mBAAA;AAAA,UACA,YAAA;AAAA,UACA,iBAAA;AAAA,SACF;AAAA,OACF,CAAA;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA,EASA,MAAM,wBAAyB,CAAA,WAAA,EAAqB,QAAkB,EAAA;AACpE,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,WAAY,CAAA,OAAM,EAAM,KAAA;AACxC,MAAA,MAAM,OAAU,GAAA,MAAM,EAAoB,CAAA,YAAY,EACnD,KAAM,CAAA,eAAA,EAAiB,QAAQ,CAAA,CAC/B,SAAS,mBAAqB,EAAA,IAAI,CAClC,CAAA,WAAA,CAAY,MAAM,WAAW,CAAA,CAAA;AAEhC,MAAI,IAAA,OAAA,CAAQ,SAAS,CAAG,EAAA;AACtB,QAAA,MAAM,GAAG,YAAY,CAAA,CAAE,QAAS,CAAA,OAAA,CAAQ,MAAM,OAAO,CAAA,CAAA;AACrD,QAAA,MAAM,EAAG,CAAA,yBAAyB,CAC/B,CAAA,MAAA,EACA,CAAA,OAAA;AAAA,UACC,mBAAA;AAAA,UACA,EAAA,CAAG,iBAAiB,CAAE,CAAA,MAAA,CAAO,IAAI,CAAE,CAAA,OAAA,CAAQ,gBAAgB,OAAO,CAAA;AAAA,SACpE,CAAA;AACF,QAAA,MAAM,GAAG,iBAAiB,CAAA,CAAE,QAAS,CAAA,OAAA,CAAQ,gBAAgB,OAAO,CAAA,CAAA;AAAA,OACtE;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA,EAQA,MAAM,sBAAsB,QAAkB,EAAA;AAC5C,IAAA,OAAO,MAAM,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,OAAM,EAAM,KAAA;AAC/C,MAAM,MAAA,YAAA,GAAiC,MAAM,EAAA,CAAG,YAAY,CAAA,CACzD,OAAO,IAAI,CAAA,CACX,KAAM,CAAA,eAAA,EAAiB,QAAQ,CAAA,CAAA;AAElC,MAAM,MAAA,OAAA,GACJ,YAAa,CAAA,MAAA,GAAS,CAClB,GAAA,MAAM,GAAG,iBAAiB,CAAA,CACvB,MAAO,CAAA,IAAI,CACX,CAAA,OAAA;AAAA,QACC,cAAA;AAAA,QACA,YAAa,CAAA,GAAA,CAAI,CAAS,KAAA,KAAA,KAAA,CAAM,EAAE,CAAA;AAAA,UAEtC,EAAC,CAAA;AAEP,MAAM,MAAA,aAAA,GACJ,OAAQ,CAAA,MAAA,GAAS,CACb,GAAA,MAAM,GAAG,yBAAyB,CAAA,CAC/B,MAAO,CAAA,IAAI,CACX,CAAA,OAAA;AAAA,QACC,mBAAA;AAAA,QACA,OAAQ,CAAA,GAAA,CAAI,CAAS,KAAA,KAAA,KAAA,CAAM,EAAE,CAAA;AAAA,UAEjC,EAAC,CAAA;AAEP,MAAM,MAAA,mBAAA,GAAsB,MAAM,IAAK,CAAA,kBAAA;AAAA,QACrC,EAAA;AAAA,QACA,aAAA;AAAA,OACF,CAAA;AAEA,MAAM,MAAA,YAAA,GACJ,QAAQ,MAAS,GAAA,CAAA,GACb,MAAM,EAAG,CAAA,iBAAiB,CACvB,CAAA,MAAA,EACA,CAAA,OAAA;AAAA,QACC,cAAA;AAAA,QACA,YAAa,CAAA,GAAA,CAAI,CAAS,KAAA,KAAA,KAAA,CAAM,EAAE,CAAA;AAAA,OAEtC,GAAA,CAAA,CAAA;AAEN,MAAM,MAAA,iBAAA,GAAoB,MAAM,EAAG,CAAA,YAAY,EAC5C,MAAO,EAAA,CACP,KAAM,CAAA,eAAA,EAAiB,QAAQ,CAAA,CAAA;AAElC,MAAM,MAAA,cAAA,GAAiB,IAAI,IAAK,EAAA,CAAA;AAChC,MAAA,cAAA,CAAe,QAAQ,cAAe,CAAA,OAAA,KAAY,EAAK,GAAA,EAAA,GAAK,KAAK,GAAI,CAAA,CAAA;AAErE,MAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,QAC/B,IAAIA,OAAG,EAAA;AAAA,QACP,WAAa,EAAA,MAAA;AAAA,QACb,aAAe,EAAA,QAAA;AAAA,QACf,cAAA;AAAA,QACA,sBAAA,EAAwB,IAAI,IAAK,EAAA;AAAA,QACjC,MAAQ,EAAA,SAAA;AAAA,QACR,iBAAmB,EAAA,MAAA;AAAA,OACpB,CAAA,CAAA;AAED,MAAA,OAAO,EAAE,QAAA,EAAU,iBAAmB,EAAA,YAAA,EAAc,mBAAoB,EAAA,CAAA;AAAA,KACzE,CAAA,CAAA;AAAA,GACH;AAAA,EAOA,MAAM,8BAA8B,QAAkB,EAAA;AACpD,IAAA,MAAM,cAAcA,OAAG,EAAA,CAAA;AACvB,IAAA,MAAM,UAAa,GAAA,QAAA,CAAA;AACnB,IAAI,IAAA;AACF,MAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,QAC/B,EAAI,EAAA,WAAA;AAAA,QACJ,WAAa,EAAA,UAAA;AAAA,QACb,aAAe,EAAA,QAAA;AAAA,QACf,MAAQ,EAAA,UAAA;AAAA,QACR,iBAAmB,EAAA,MAAA;AAAA,OACpB,CAAA,CAAA;AACD,MAAO,OAAA,EAAE,aAAa,UAAY,EAAA,QAAA,EAAU,GAAG,YAAc,EAAA,IAAA,CAAK,KAAM,EAAA,CAAA;AAAA,aACjE,EAAP,EAAA;AAEA,MAAO,OAAA,KAAA,CAAA,CAAA;AAAA,KACT;AAAA,GACF;AAAA,EAQA,MAAM,cAAe,CAAA,QAAA,EAAkB,WAAqB,EAAA;AAC1D,IAAA,OAAO,MAAM,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,OAAM,EAAM,KAAA;AAC/C,MAAA,MAAM,UAA6C,MAAM,EAAA;AAAA,QACvD,gBAAA;AAAA,OAEC,CAAA,MAAA;AAAA,QACC,EAAG,CAAA,GAAA,CAAI,cAAc,CAAA,CAAE,GAAG,QAAQ,CAAA;AAAA,QAClC,EAAG,CAAA,GAAA,CAAI,0BAA0B,CAAA,CAAE,GAAG,KAAK,CAAA;AAAA,OAE5C,CAAA,IAAA;AAAA,QACC,eAAA;AAAA,QACA,yBAAA;AAAA,QACA,0BAAA;AAAA,OAED,CAAA,IAAA,CAAK,QAAU,EAAA,kBAAA,EAAoB,0BAA0B,CAC7D,CAAA,UAAA;AAAA,QACC,YAAA;AAAA,QACA,EAAA,CAAG,iBAAiB,CACjB,CAAA,IAAA;AAAA,UACC,yBAAA;AAAA,UACA,oBAAA;AAAA,UACA,2CAAA;AAAA,UAED,MAAO,CAAA,6BAA6B,CACpC,CAAA,KAAA,CAAM,gCAAgC,WAAW,CAAA;AAAA,OAErD,CAAA,QAAA;AAAA,QACC,YAAA;AAAA,QACA,CAAwB,qBAAA,EAAA,sCAAA,CAAA,CAAA;AAAA,OAC1B,CACC,QAAS,CAAA,cAAA,EAAgB,QAAQ,CAAA,CAAA;AACpC,MAAO,OAAA,OAAA,CAAQ,IAAI,CAAU,MAAA,KAAA;AAC3B,QAAA,OAAO,EAAE,MAAQ,EAAA,IAAA,CAAK,KAAM,CAAA,MAAA,CAAO,MAAM,CAAE,EAAA,CAAA;AAAA,OAC5C,CAAA,CAAA;AAAA,KACF,CAAA,CAAA;AAAA,GACH;AAAA,EAMA,MAAM,WAAc,GAAA;AAClB,IAAA,OAAO,MAAM,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,OAAM,EAAM,KAAA;AAC/C,MAAA,MAAM,UAAU,MAAM,EAAA;AAAA,QACpB,YAAA;AAAA,QAEC,QAAS,CAAA,IAAA,EAAM,eAAe,CAC9B,CAAA,KAAA,CAAM,qBAAqB,IAAI,CAAA,CAAA;AAClC,MAAO,OAAA,OAAA,CAAA;AAAA,KACR,CAAA,CAAA;AAAA,GACH;AAAA,EAMA,MAAM,0BAA0B,QAAkB,EAAA;AAChD,IAAM,MAAA,IAAA,CAAK,gCAAgC,QAAU,EAAA;AAAA,MACnD,cAAA,EAAgB,IAAI,IAAK,EAAA;AAAA,KAC1B,CAAA,CAAA;AAAA,GACH;AAAA,EAWA,MAAM,gBAAmB,GAAA;AACvB,IAAM,MAAA,SAAA,GAAY,MAAM,IAAA,CAAK,aAAc,EAAA,CAAA;AAE3C,IAAA,MAAM,iBAAoB,GAAA,MAAM,IAAK,CAAA,UAAA,CAAW,YAAY,CAAA,CAAA;AAE5D,IAAM,MAAA,cAAA,GAAiB,IAAI,IAAK,EAAA,CAAA;AAChC,IAAA,cAAA,CAAe,QAAQ,cAAe,CAAA,OAAA,KAAY,EAAK,GAAA,EAAA,GAAK,KAAK,GAAI,CAAA,CAAA;AAErE,IAAA,KAAA,MAAW,YAAY,SAAW,EAAA;AAChC,MAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,QAC/B,IAAIA,OAAG,EAAA;AAAA,QACP,WAAa,EAAA,MAAA;AAAA,QACb,aAAe,EAAA,QAAA;AAAA,QACf,cAAA;AAAA,QACA,sBAAA,EAAwB,IAAI,IAAK,EAAA;AAAA,QACjC,MAAQ,EAAA,SAAA;AAAA,QACR,iBAAmB,EAAA,MAAA;AAAA,OACpB,CAAA,CAAA;AAAA,KACH;AAEA,IAAA,MAAM,qBAAwB,GAAA,MAAM,IAAK,CAAA,UAAA,CAAW,iBAAiB,CAAA,CAAA;AACrE,IAAM,MAAA,mBAAA,GAAsB,MAAM,IAAK,CAAA,UAAA;AAAA,MACrC,yBAAA;AAAA,KACF,CAAA;AAEA,IAAO,OAAA,EAAE,iBAAmB,EAAA,qBAAA,EAAuB,mBAAoB,EAAA,CAAA;AAAA,GACzE;AAAA,EAMA,MAAM,qBAAqB,WAAqB,EAAA;AAC9C,IAAA,MAAM,KAAK,yBAA0B,CAAA;AAAA,MACnC,WAAA;AAAA,MACA,MAAA,EAAQ,EAAE,WAAA,EAAa,QAAS,EAAA;AAAA,KACjC,CAAA,CAAA;AAAA,GACH;AAAA,EAMA,MAAM,oBAAoB,WAAqB,EAAA;AAC7C,IAAA,MAAM,KAAK,yBAA0B,CAAA;AAAA,MACnC,WAAA;AAAA,MACA,MAAA,EAAQ,EAAE,MAAA,EAAQ,UAAW,EAAA;AAAA,KAC9B,CAAA,CAAA;AAAA,GACH;AAAA,EAMA,MAAM,oBAAoB,WAAqB,EAAA;AAC7C,IAAA,MAAM,KAAK,yBAA0B,CAAA;AAAA,MACnC,WAAA;AAAA,MACA,MAAQ,EAAA;AAAA,QACN,WAAa,EAAA,gBAAA;AAAA,QACb,iBAAA,EAAmB,IAAI,IAAK,EAAA;AAAA,QAC5B,MAAQ,EAAA,UAAA;AAAA,QACR,mBAAmBA,OAAG,EAAA;AAAA,OACxB;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA,EAOA,MAAM,kBAAmB,CAAA,WAAA,EAAqB,UAAsB,EAAA;AAClE,IAAA,MAAM,KAAK,yBAA0B,CAAA;AAAA,MACnC,WAAA;AAAA,MACA,MAAQ,EAAA;AAAA,QACN,WAAa,EAAA,MAAA;AAAA,QACb,cAAA,EAAgB,IAAI,IAAK,CAAA,IAAA,CAAK,KAAQ,GAAA,UAAA,CAAW,EAAG,CAAA,cAAc,CAAC,CAAA;AAAA,QACnE,sBAAA,EAAwB,IAAI,IAAK,EAAA;AAAA,QACjC,MAAQ,EAAA,SAAA;AAAA,OACV;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA,EAMA,MAAM,wBAAwB,WAAqB,EAAA;AACjD,IAAA,MAAM,KAAK,yBAA0B,CAAA;AAAA,MACnC,WAAA;AAAA,MACA,MAAQ,EAAA,EAAE,QAAU,EAAA,CAAA,EAAG,QAAQ,cAAe,EAAA;AAAA,KAC/C,CAAA,CAAA;AAAA,GACH;AAAA,EAOA,MAAM,oBAAqB,CAAA,WAAA,EAAqB,OAAkB,EAAA;AAChE,IAAA,MAAM,MAAmC,GAAA;AAAA,MACvC,WAAa,EAAA,QAAA;AAAA,MACb,UAAA,EAAY,UAAU,OAAU,GAAA,KAAA,CAAA;AAAA,MAChC,cAAA,EAAgB,IAAI,IAAK,EAAA;AAAA,MACzB,MAAQ,EAAA,WAAA;AAAA,KACV,CAAA;AACA,IAAA,MAAM,IAAK,CAAA,yBAAA,CAA0B,EAAE,WAAA,EAAa,QAAQ,CAAA,CAAA;AAAA,GAC9D;AAAA,EAMA,MAAM,oBAAoB,WAAqB,EAAA;AAC7C,IAAA,MAAM,KAAK,yBAA0B,CAAA;AAAA,MACnC,WAAA;AAAA,MACA,MAAQ,EAAA;AAAA,QACN,WAAa,EAAA,oBAAA;AAAA,QACb,iBAAA,EAAmB,IAAI,IAAK,EAAA;AAAA,QAC5B,MAAQ,EAAA,UAAA;AAAA,QACR,mBAAmBA,OAAG,EAAA;AAAA,OACxB;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA,EASA,MAAM,kBAAA,CACJ,WACA,EAAA,QAAA,EACA,OACA,aACA,EAAA;AACA,IAAA,MAAM,KAAK,yBAA0B,CAAA;AAAA,MACnC,WAAA;AAAA,MACA,MAAQ,EAAA;AAAA,QACN,WAAa,EAAA,SAAA;AAAA,QACb,UAAU,QAAW,GAAA,CAAA;AAAA,QACrB,UAAA,EAAY,OAAO,KAAK,CAAA;AAAA,QACxB,gBAAgB,IAAI,IAAA,CAAK,IAAK,CAAA,GAAA,KAAQ,aAAa,CAAA;AAAA,QACnD,MAAQ,EAAA,aAAA;AAAA,OACV;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA,EAOA,MAAM,YAAY,WAAqB,EAAA;AACrC,IAAA,OAAO,MAAM,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,OAAM,EAAM,KAAA;AAC/C,MAAA,MAAM,IAAO,GAAA,MAAM,EAAe,CAAA,iBAAiB,CAChD,CAAA,KAAA,CAAM,cAAgB,EAAA,WAAW,CACjC,CAAA,OAAA,CAAQ,UAAY,EAAA,MAAM,EAC1B,KAAM,EAAA,CAAA;AACT,MAAO,OAAA,IAAA,CAAA;AAAA,KACR,CAAA,CAAA;AAAA,GACH;AAAA,EAEA,MAAM,YAAY,WAAqB,EAAA;AACrC,IAAA,OAAO,MAAM,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,OAAM,EAAM,KAAA;AAC/C,MAAM,MAAA,KAAA,GAAQ,MAAM,EAAA,CAAe,iBAAiB,CAAA,CACjD,KAAM,CAAA,cAAA,EAAgB,WAAW,CAAA,CACjC,OAAQ,CAAA,UAAA,EAAY,MAAM,CAAA,CAAA;AAC7B,MAAO,OAAA,KAAA,CAAA;AAAA,KACR,CAAA,CAAA;AAAA,GACH;AAAA,EAMA,MAAM,WAAW,OAA2B,EAAA;AAC1C,IAAM,MAAA,EAAE,QAAW,GAAA,OAAA,CAAA;AACnB,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,WAAY,CAAA,OAAM,EAAM,KAAA;AACxC,MAAA,MAAM,EAAG,CAAA,iBAAiB,CAAE,CAAA,MAAA,CAAO,MAAM,CAAA,CAAA;AAAA,KAC1C,CAAA,CAAA;AAAA,GACH;AAAA,EAMA,MAAM,kBAAmB,CAAA,MAAA,EAAgB,QAA4B,EAAA;AACnE,IAAA,MAAM,IAAK,CAAA,MAAA,CAAO,WAAY,CAAA,OAAM,EAAM,KAAA;AACxC,MAAM,MAAA,EAAA,CAAG,yBAAyB,CAAE,CAAA,MAAA;AAAA,QAClC,QAAA,CAAS,IAAI,CAAW,MAAA,MAAA;AAAA,UACtB,IAAIA,OAAG,EAAA;AAAA,UACP,iBAAmB,EAAA,MAAA;AAAA,UACnB,GAAA,EAAKC,+BAAmB,CAAA,MAAA,CAAO,MAAM,CAAA;AAAA,SACrC,CAAA,CAAA;AAAA,OACJ,CAAA;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA,EAOA,MAAM,WAAW,KAAe,EAAA;AAC9B,IAAA,OAAO,MAAM,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,OAAM,EAAM,KAAA;AAC/C,MAAA,OAAO,MAAM,EAAA,CAAG,KAAK,CAAA,CAAE,MAAO,EAAA,CAAA;AAAA,KAC/B,CAAA,CAAA;AAAA,GACH;AAAA,EAMA,MAAM,aAAgB,GAAA;AACpB,IAAA,OAAO,MAAM,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,OAAM,EAAM,KAAA;AAC/C,MAAA,MAAM,YAAY,MAAM,EAAA;AAAA,QACtB,YAAA;AAAA,OACF,CAAE,SAAS,eAAe,CAAA,CAAA;AAC1B,MAAA,OAAO,SAAU,CAAA,GAAA,CAAI,CAAS,KAAA,KAAA,KAAA,CAAM,aAAa,CAAA,CAAA;AAAA,KAClD,CAAA,CAAA;AAAA,GACH;AAAA,EAEA,MAAM,YAAa,CAAA,QAAA,EAAkB,MAAkC,EAAA;AACrE,IAAM,MAAA,IAAA,CAAK,+BAAgC,CAAA,QAAA,EAAU,MAAM,CAAA,CAAA;AAAA,GAC7D;AACF;;AC1jBO,MAAM,mBAAsB,GAAA,wCAAA;;ACInC,eAAsB,wBAAwB,IAA2B,EAAA;AACvE,EAAA,MAAM,aAAgB,GAAAC,gCAAA;AAAA,IACpB,gEAAA;AAAA,IACA,YAAA;AAAA,GACF,CAAA;AAEA,EAAM,MAAA,IAAA,CAAK,QAAQ,MAAO,CAAA;AAAA,IACxB,SAAW,EAAA,aAAA;AAAA,IACX,SAAW,EAAA,mBAAA;AAAA,GACZ,CAAA,CAAA;AACH;;ACFO,MAAM,0BAAsD,CAAA;AAAA,EAMjE,YAAoB,OAAiC,EAAA;AAAjC,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA,CAAA;AAlCtB,IAAA,IAAA,EAAA,CAAA;AAmCI,IAAA,IAAA,CAAK,UAAU,OAAQ,CAAA,OAAA,CAAA;AACvB,IAAA,IAAA,CAAK,UAAa,GAAAC,cAAA,CAAS,UAAW,CAAA,OAAA,CAAQ,UAAU,CAAA,CAAA;AACxD,IAAK,IAAA,CAAA,OAAA,GAAA,CAAU,EAAQ,GAAA,OAAA,CAAA,OAAA,KAAR,IAAmB,GAAA,EAAA,GAAA;AAAA,MAChC,EAAE,SAAS,CAAE,EAAA;AAAA,MACb,EAAE,SAAS,CAAE,EAAA;AAAA,MACb,EAAE,SAAS,EAAG,EAAA;AAAA,MACd,EAAE,OAAO,CAAE,EAAA;AAAA,KACb,CAAA;AAAA,GACF;AAAA,EAEA,MAAM,OAAO,MAAqB,EAAA;AAChC,IAAI,IAAA;AACF,MAAK,IAAA,CAAA,OAAA,CAAQ,MAAO,CAAA,KAAA,CAAM,YAAY,CAAA,CAAA;AACtC,MAAM,MAAA,IAAA,CAAK,iBAAiB,MAAM,CAAA,CAAA;AAAA,aAC3B,KAAP,EAAA;AACA,MAAA,IAAA,CAAK,OAAQ,CAAA,MAAA,CAAO,KAAM,CAAA,CAAA,EAAG,KAAO,CAAA,CAAA,CAAA,CAAA;AACpC,MAAM,MAAA,KAAA,CAAA;AAAA,KACN,SAAA;AACA,MAAK,IAAA,CAAA,OAAA,CAAQ,MAAO,CAAA,KAAA,CAAM,UAAU,CAAA,CAAA;AAAA,KACtC;AAAA,GACF;AAAA,EAEA,MAAM,iBAAiB,MAAqB,EAAA;AAC1C,IAAA,MAAM,KAAK,OAAQ,CAAA,KAAA,CAAA;AAEnB,IAAM,MAAA,MAAA,GAAS,MAAM,IAAA,CAAK,gBAAiB,EAAA,CAAA;AAC3C,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,MAAM,EAAE,WAAA,EAAa,YAAc,EAAA,UAAA,EAAY,UAAa,GAAA,MAAA,CAAA;AAE5D,MAAA,QAAQ,UAAY;AAAA,QAClB,KAAK,MAAA;AACH,UAAI,IAAA,IAAA,CAAK,GAAI,EAAA,GAAI,YAAc,EAAA;AAC7B,YAAA,MAAM,KAAK,OAAQ,CAAA,uBAAA;AAAA,cACjB,IAAA,CAAK,OAAQ,CAAA,QAAA,CAAS,eAAgB,EAAA;AAAA,aACxC,CAAA;AACA,YAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,cAClB,CAAiC,8BAAA,EAAA,WAAA,CAAA,iDAAA,CAAA;AAAA,aACnC,CAAA;AAEA,YAAM,MAAA,IAAA,CAAK,OAAQ,CAAA,mBAAA,CAAoB,WAAW,CAAA,CAAA;AAAA,WAC7C,MAAA;AACL,YAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,cAClB,CAAkC,+BAAA,EAAA,WAAA,CAAA,wBAAA,CAAA;AAAA,aACpC,CAAA;AAAA,WACF;AACA,UAAA,MAAA;AAAA,QACF,KAAK,QAAA;AACH,UAAI,IAAA;AACF,YAAM,MAAA,IAAA,CAAK,OAAQ,CAAA,mBAAA,CAAoB,WAAW,CAAA,CAAA;AAClD,YAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,cAAA,CAAe,aAAa,MAAM,CAAA,CAAA;AAC1D,YAAA,IAAI,IAAM,EAAA;AACR,cAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,gBAClB,CAAkC,+BAAA,EAAA,WAAA,CAAA,4CAAA,EAA0D,IAAK,CAAA,UAAA,CAAW,OAAQ,EAAA,CAAA,CAAA;AAAA,eACtH,CAAA;AACA,cAAA,MAAM,KAAK,OAAQ,CAAA,kBAAA;AAAA,gBACjB,WAAA;AAAA,gBACA,IAAK,CAAA,UAAA;AAAA,eACP,CAAA;AAAA,aACK,MAAA;AACL,cAAM,MAAA,IAAA,CAAK,OAAQ,CAAA,uBAAA,CAAwB,WAAW,CAAA,CAAA;AACtD,cAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,gBAClB,CAAkC,+BAAA,EAAA,WAAA,CAAA,YAAA,CAAA;AAAA,eACpC,CAAA;AAAA,aACF;AAAA,mBACO,KAAP,EAAA;AACA,YAAA,IACG,KAAgB,CAAA,OAAA,IAChB,KAAgB,CAAA,OAAA,KAAY,QAC7B,EAAA;AACA,cAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,gBAClB,CAAkC,+BAAA,EAAA,WAAA,CAAA,UAAA,CAAA;AAAA,eACpC,CAAA;AACA,cAAA,MAAM,KAAK,OAAQ,CAAA,oBAAA;AAAA,gBACjB,WAAA;AAAA,gBACC,KAAgB,CAAA,OAAA;AAAA,eACnB,CAAA;AAAA,aACK,MAAA;AACL,cAAA,MAAM,iBAAiBA,cAAS,CAAA,UAAA;AAAA,gBAC9B,IAAA,CAAK,QAAQ,IAAK,CAAA,GAAA,CAAI,KAAK,OAAQ,CAAA,MAAA,GAAS,GAAG,QAAQ,CAAA,CAAA;AAAA,eACzD,CAAA;AAEA,cAAM,MAAA,aAAA,GAAgB,cAAe,CAAA,EAAA,CAAG,cAAc,CAAA,CAAA;AACtD,cAAK,IAAA,CAAA,OAAA,CAAQ,MAAO,CAAA,KAAA,CAAM,KAAK,CAAA,CAAA;AAE/B,cAAA,MAAM,iBAAiBC,qBAAe,CAAA,KAAK,CAAE,CAAA,SAAA,CAAU,GAAG,GAAG,CAAA,CAAA;AAC7D,cAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,KAAA;AAAA,gBAClB,CAAkC,+BAAA,EAAA,WAAA,CAAA,oEAAA,EAAkF,cAAe,CAAA,OAAA,EAAc,CAAA,EAAA,EAAA,cAAA,CAAA,CAAA,CAAA;AAAA,eACnJ,CAAA;AAEA,cAAA,MAAM,KAAK,OAAQ,CAAA,kBAAA;AAAA,gBACjB,WAAA;AAAA,gBACA,QAAA;AAAA,gBACA,KAAA;AAAA,gBACA,aAAA;AAAA,eACF,CAAA;AAAA,aACF;AAAA,WACF;AACA,UAAA,MAAA;AAAA,QACF,KAAK,SAAA;AACH,UAAI,IAAA,IAAA,CAAK,GAAI,EAAA,GAAI,YAAc,EAAA;AAC7B,YAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,cAClB,CAAkC,+BAAA,EAAA,WAAA,CAAA,0CAAA,CAAA;AAAA,aACpC,CAAA;AACA,YAAM,MAAA,IAAA,CAAK,OAAQ,CAAA,oBAAA,CAAqB,WAAW,CAAA,CAAA;AAAA,WAC9C,MAAA;AACL,YAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,cAClB,CAAkC,+BAAA,EAAA,WAAA,CAAA,oBAAA,CAAA;AAAA,aACpC,CAAA;AAAA,WACF;AACA,UAAA,MAAA;AAAA,QACF,KAAK,QAAA;AACH,UAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,YAClB,CAAkC,+BAAA,EAAA,WAAA,CAAA,yBAAA,CAAA;AAAA,WACpC,CAAA;AACA,UAAM,MAAA,IAAA,CAAK,OAAQ,CAAA,mBAAA,CAAoB,WAAW,CAAA,CAAA;AAClD,UAAA,MAAA;AAAA,QACF;AACE,UAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,KAAA;AAAA,YAClB,kCAAkC,WAAyC,CAAA,2BAAA,EAAA,UAAA,CAAA,CAAA,CAAA;AAAA,WAC7E,CAAA;AAAA,OACJ;AAAA,KACK,MAAA;AACL,MAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,KAAA;AAAA,QAClB,CAAuF,oFAAA,EAAA,IAAA,CAAK,OAAQ,CAAA,QAAA,CAAS,eAAgB,EAAA,CAAA,EAAA,CAAA;AAAA,OAC/H,CAAA;AAAA,KACF;AAAA,GACF;AAAA,EAEA,MAAM,gBAAmB,GAAA;AACvB,IAAA,MAAM,YAAe,GAAA,IAAA,CAAK,OAAQ,CAAA,QAAA,CAAS,eAAgB,EAAA,CAAA;AAC3D,IAAA,MAAM,MAAS,GAAA,MAAM,IAAK,CAAA,OAAA,CAAQ,0BAA0B,YAAY,CAAA,CAAA;AACxE,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,QAClB,gDAAgD,MAAO,CAAA,EAAA,CAAA,CAAA,CAAA;AAAA,OACzD,CAAA;AACA,MAAO,OAAA;AAAA,QACL,aAAa,MAAO,CAAA,EAAA;AAAA,QACpB,YAAY,MAAO,CAAA,WAAA;AAAA,QACnB,UAAU,MAAO,CAAA,QAAA;AAAA,QACjB,YAAA,EAAc,MAAO,CAAA,cAAA,CAAe,OAAQ,EAAA;AAAA,OAC9C,CAAA;AAAA,KACF;AACA,IAAM,MAAA,MAAA,GAAS,MAAM,IAAA,CAAK,OAAQ,CAAA,6BAAA;AAAA,MAChC,YAAA;AAAA,KACF,CAAA;AACA,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,QAClB,kDAAkD,MAAO,CAAA,WAAA,CAAA,CAAA,CAAA;AAAA,OAC3D,CAAA;AAAA,KACF;AACA,IAAO,OAAA,MAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,cAAe,CAAA,EAAA,EAAY,MAAqB,EAAA;AACpD,IAAA,MAAM,QAAW,GAAA,MAAM,IAAK,CAAA,OAAA,CAAQ,YAAY,EAAE,CAAA,CAAA;AAElD,IAAM,MAAA,MAAA,GAAS,QAAW,GAAA,QAAA,CAAS,MAAS,GAAA,KAAA,CAAA,CAAA;AAC5C,IAAA,IAAI,QAAW,GAAA,QAAA,GAAW,QAAS,CAAA,QAAA,GAAW,CAAI,GAAA,CAAA,CAAA;AAElD,IAAM,MAAA,KAAA,GAAQC,uBAAY,GAAI,EAAA,CAAA;AAC9B,IAAA,IAAI,KAAQ,GAAA,CAAA,CAAA;AACZ,IAAA,IAAI,IAAO,GAAA,KAAA,CAAA;AACX,IAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,MAClB,CAAkC,+BAAA,EAAA,EAAA,CAAA,iBAAA,CAAA;AAAA,KACpC,CAAA;AAEA,IAAA,MAAM,IAAK,CAAA,OAAA,CAAQ,QAAS,CAAA,MAAA,CAAO,OAAO,OAAqB,KAAA;AAC7D,MAAA,IAAI,OAAO,MAAM,IAAA,CAAK,QAAQ,QAAS,CAAA,IAAA,CAAK,SAAS,MAAM,CAAA,CAAA;AAC3D,MAAA,KAAA,EAAA,CAAA;AACA,MAAS,WAAA;AACP,QAAA,IAAA,GAAO,IAAK,CAAA,IAAA,CAAA;AACZ,QAAA,MAAM,KAAK,IAAK,CAAA;AAAA,UACd,EAAA;AAAA,UACA,QAAA;AAAA,UACA,UAAU,IAAM,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,IAAA,CAAA,QAAA;AAAA,UAChB,MAAM,IAAK,CAAA,IAAA;AAAA,UACX,QAAQ,IAAM,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,IAAA,CAAA,MAAA;AAAA,SACf,CAAA,CAAA;AACD,QAAI,IAAA,MAAA,CAAO,OAAW,IAAA,IAAA,CAAK,IAAM,EAAA;AAC/B,UAAA,MAAA;AAAA,SACK,MAAA;AACL,UAAA,IAAA,GAAO,MAAM,IAAK,CAAA,OAAA,CAAQ,SAAS,IAAK,CAAA,OAAA,EAAS,KAAK,MAAM,CAAA,CAAA;AAC5D,UAAA,KAAA,EAAA,CAAA;AACA,UAAA,QAAA,EAAA,CAAA;AAAA,SACF;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAED,IAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA;AAAA,MAClB,CAAA,+BAAA,EAAkC,EAAwB,CAAA,mBAAA,EAAA,KAAA,CAAA,YAAA,EAAoB,IAAK,CAAA,KAAA;AAAA,QACjFA,sBAAA,CAAY,KAAQ,GAAA,KAAA;AAAA,OACtB,CAAA,IAAA,CAAA;AAAA,KACF,CAAA;AACA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,KAAK,OAMR,EAAA;AA7OL,IAAA,IAAA,EAAA,CAAA;AA8OI,IAAA,MAAM,EAAE,EAAI,EAAA,QAAA,EAAU,QAAU,EAAA,IAAA,EAAM,QAAW,GAAA,OAAA,CAAA;AACjD,IAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,KAAA;AAAA,MAClB,CAAA,+BAAA,EAAkC,EAChC,CAAA,QAAA,EAAA,QAAA,GAAW,QAAS,CAAA,MAAA,GAAS,CAE7B,CAAA,mBAAA,EAAA,MAAA,GAAS,IAAK,CAAA,SAAA,CAAU,MAAM,CAAA,GAAI,MACzB,CAAA,QAAA,EAAA,IAAA,CAAA,CAAA;AAAA,KACb,CAAA;AACA,IAAA,MAAM,SAASL,OAAG,EAAA,CAAA;AAElB,IAAM,MAAA,IAAA,CAAK,QAAQ,UAAW,CAAA;AAAA,MAC5B,MAAQ,EAAA;AAAA,QACN,EAAI,EAAA,MAAA;AAAA,QACJ,YAAc,EAAA,EAAA;AAAA,QACd,MAAA;AAAA,QACA,QAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAED,IAAI,IAAA,QAAA,IAAY,QAAS,CAAA,MAAA,GAAS,CAAG,EAAA;AACnC,MAAA,MAAM,IAAK,CAAA,OAAA,CAAQ,kBAAmB,CAAA,MAAA,EAAQ,QAAQ,CAAA,CAAA;AAAA,KACxD;AAEA,IAAM,MAAA,KAAA,GAAA,CACJ,EAAU,GAAA,QAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,QAAA,CAAA,GAAA,CAAI,CAAa,QAAA,MAAA;AAAA,MACzB,GAAG,QAAA;AAAA,MACH,MAAQ,EAAA;AAAA,QACN,GAAG,QAAS,CAAA,MAAA;AAAA,QACZ,QAAU,EAAA;AAAA,UACR,GAAG,SAAS,MAAO,CAAA,QAAA;AAAA,UACnB,WAAa,EAAA;AAAA,YACX,GAAG,QAAS,CAAA,MAAA,CAAO,QAAS,CAAA,WAAA;AAAA,YAC5B,CAAC,sCAAA,GACC,IAAK,CAAA,OAAA,CAAQ,SAAS,eAAgB,EAAA;AAAA,WAC1C;AAAA,SACF;AAAA,OACF;AAAA,KACF,CAAA,CAAA,KAbA,YAaO,EAAC,CAAA;AAEV,IAAA,MAAM,UAA4B,EAAC,CAAA;AAEnC,IAAA,IAAI,IAAM,EAAA;AACR,MAAQ,OAAA,CAAA,IAAA;AAAA,QACN,GAAI,MAAM,IAAA,CAAK,OAAQ,CAAA,cAAA;AAAA,UACrB,IAAA,CAAK,OAAQ,CAAA,QAAA,CAAS,eAAgB,EAAA;AAAA,UACtC,EAAA;AAAA,SACF;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IAAM,MAAA,IAAA,CAAK,OAAQ,CAAA,UAAA,CAAW,aAAc,CAAA;AAAA,MAC1C,IAAM,EAAA,OAAA;AAAA,MACN,KAAA;AAAA,MACA,OAAA;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF;;ACtRO,MAAM,gBAAmB,GAAA,sBAAA,CAAA;AACzB,MAAM,eAAkB,GAAA,qBAAA,CAAA;AACxB,MAAM,kBAAqB,GAAA,kCAAA;;ACKrB,MAAA,+BAAA,GAAkC,OAC7C,OAAA,EACA,MACG,KAAA;AACH,EAAA,MAAM,SAASM,0BAAO,EAAA,CAAA;AACtB,EAAO,MAAA,CAAA,GAAA,CAAIC,2BAAQ,CAAA,IAAA,EAAM,CAAA,CAAA;AAGzB,EAAA,MAAA,CAAO,GAAI,CAAA,eAAA,EAAiB,OAAO,CAAA,EAAG,GAAQ,KAAA;AAC5C,IAAM,MAAA,OAAA,GAAU,MAAM,OAAA,CAAQ,WAAY,EAAA,CAAA;AAC1C,IAAA,MAAM,SAAY,GAAA,OAAA,CAAQ,GAAI,CAAA,CAAA,MAAA,KAAU,OAAO,aAAa,CAAA,CAAA;AAC5D,IAAA,MAAM,UAAa,GAAA;AAAA,MACjB,GAAG,IAAI,GAAI,CAAA,SAAA,CAAU,OAAO,CAAC,CAAA,EAAG,CAAG,EAAA,CAAA,KAAM,CAAE,CAAA,OAAA,CAAQ,CAAC,CAAA,KAAM,CAAC,CAAC,CAAA;AAAA,KAC9D,CAAA;AAEA,IAAI,IAAA,UAAA,CAAW,SAAS,CAAG,EAAA;AACzB,MAAA,GAAA,CAAI,KAAK,EAAE,OAAA,EAAS,KAAO,EAAA,mBAAA,EAAqB,YAAY,CAAA,CAAA;AAAA,KACvD,MAAA;AACL,MAAA,GAAA,CAAI,IAAK,CAAA,EAAE,OAAS,EAAA,IAAA,EAAM,CAAA,CAAA;AAAA,KAC5B;AAAA,GACD,CAAA,CAAA;AAGD,EAAA,MAAA,CAAO,IAAK,CAAA,gBAAA,EAAkB,OAAO,CAAA,EAAG,GAAQ,KAAA;AAC9C,IAAM,MAAA,MAAA,GAAS,MAAM,OAAA,CAAQ,gBAAiB,EAAA,CAAA;AAC9C,IAAA,GAAA,CAAI,KAAK,MAAM,CAAA,CAAA;AAAA,GAChB,CAAA,CAAA;AAGD,EAAA,MAAA,CAAO,GAAI,CAAA,kBAAA,EAAoB,OAAO,GAAA,EAAK,GAAQ,KAAA;AACjD,IAAM,MAAA,EAAE,QAAS,EAAA,GAAI,GAAI,CAAA,MAAA,CAAA;AACzB,IAAA,MAAM,MAAS,GAAA,MAAM,OAAQ,CAAA,yBAAA,CAA0B,QAAQ,CAAA,CAAA;AAC/D,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,GAAA,CAAI,IAAK,CAAA;AAAA,QACP,OAAS,EAAA,IAAA;AAAA,QACT,MAAQ,EAAA;AAAA,UACN,gBAAgB,MAAO,CAAA,MAAA;AAAA,UACvB,cAAgB,EAAA,IAAI,IAAK,CAAA,MAAA,CAAO,cAAc,CAAA;AAAA,SAChD;AAAA,QACA,YAAY,MAAO,CAAA,UAAA;AAAA,OACpB,CAAA,CAAA;AAAA,KACI,MAAA;AACL,MAAM,MAAA,SAAA,GAAsB,MAAM,OAAA,CAAQ,aAAc,EAAA,CAAA;AACxD,MAAI,IAAA,SAAA,CAAU,QAAS,CAAA,QAAQ,CAAG,EAAA;AAChC,QAAA,GAAA,CAAI,IAAK,CAAA;AAAA,UACP,OAAS,EAAA,IAAA;AAAA,UACT,MAAQ,EAAA;AAAA,YACN,cAAgB,EAAA,iCAAA;AAAA,WAClB;AAAA,SACD,CAAA,CAAA;AAAA,OACI,MAAA;AACL,QAAO,MAAA,CAAA,KAAA;AAAA,UACL,CAAG,EAAA,QAAA,CAAA,6CAAA,CAAA;AAAA,SACL,CAAA;AACA,QAAI,GAAA,CAAA,MAAA,CAAO,GAAG,CAAA,CAAE,IAAK,CAAA;AAAA,UACnB,OAAS,EAAA,KAAA;AAAA,UACT,QAAQ,EAAC;AAAA,UACT,YAAY,CAAa,UAAA,EAAA,QAAA,CAAA,WAAA,CAAA;AAAA,SAC1B,CAAA,CAAA;AAAA,OACH;AAAA,KACF;AAAA,GACD,CAAA,CAAA;AAGD,EAAA,MAAA,CAAO,IAAK,CAAA,CAAA,EAAG,kBAA8B,CAAA,QAAA,CAAA,EAAA,OAAO,KAAK,GAAQ,KAAA;AAC/D,IAAM,MAAA,EAAE,QAAS,EAAA,GAAI,GAAI,CAAA,MAAA,CAAA;AACzB,IAAA,MAAM,MAAS,GAAA,MAAM,OAAQ,CAAA,yBAAA,CAA0B,QAAQ,CAAA,CAAA;AAC/D,IAAA,IAAI,MAAQ,EAAA;AACV,MAAM,MAAA,OAAA,CAAQ,0BAA0B,QAAQ,CAAA,CAAA;AAChD,MAAA,GAAA,CAAI,IAAK,CAAA;AAAA,QACP,OAAS,EAAA,IAAA;AAAA,QACT,SAAS,CAAG,EAAA,QAAA,CAAA,wBAAA,CAAA;AAAA,OACb,CAAA,CAAA;AAAA,KACI,MAAA;AACL,MAAM,MAAA,SAAA,GAAsB,MAAM,OAAA,CAAQ,aAAc,EAAA,CAAA;AACxD,MAAI,IAAA,SAAA,CAAU,QAAS,CAAA,QAAQ,CAAG,EAAA;AAChC,QAAO,MAAA,CAAA,KAAA,CAAM,GAAG,QAAmC,CAAA,yBAAA,CAAA,CAAA,CAAA;AACnD,QAAA,GAAA,CAAI,IAAK,CAAA;AAAA,UACP,OAAS,EAAA,IAAA;AAAA,UACT,OAAS,EAAA,wDAAA;AAAA,SACV,CAAA,CAAA;AAAA,OACI,MAAA;AACL,QAAI,GAAA,CAAA,MAAA,CAAO,GAAG,CAAA,CAAE,IAAK,CAAA;AAAA,UACnB,OAAS,EAAA,KAAA;AAAA,UACT,SAAS,CAAa,UAAA,EAAA,QAAA,CAAA,WAAA,CAAA;AAAA,SACvB,CAAA,CAAA;AAAA,OACH;AAAA,KACF;AAAA,GACD,CAAA,CAAA;AAID,EAAA,MAAA,CAAO,IAAK,CAAA,CAAA,EAAG,kBAA4B,CAAA,MAAA,CAAA,EAAA,OAAO,KAAK,GAAQ,KAAA;AAC7D,IAAM,MAAA,EAAE,QAAS,EAAA,GAAI,GAAI,CAAA,MAAA,CAAA;AAEzB,IAAA,MAAM,MAAS,GAAA,MAAM,OAAQ,CAAA,yBAAA,CAA0B,QAAQ,CAAA,CAAA;AAC/D,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,MAAM,cAAc,MAAO,CAAA,EAAA,CAAA;AAC3B,MAAI,IAAA,MAAA,CAAO,WAAW,SAAW,EAAA;AAC/B,QAAM,MAAA,OAAA,CAAQ,oBAAoB,WAAW,CAAA,CAAA;AAAA,OACxC,MAAA;AACL,QAAM,MAAA,OAAA,CAAQ,qBAAqB,WAAW,CAAA,CAAA;AAAA,OAChD;AACA,MAAA,GAAA,CAAI,IAAK,CAAA;AAAA,QACP,OAAS,EAAA,IAAA;AAAA,QACT,SAAS,CAAG,EAAA,QAAA,CAAA,uBAAA,CAAA;AAAA,OACb,CAAA,CAAA;AAAA,KACI,MAAA;AACL,MAAM,MAAA,SAAA,GAAsB,MAAM,OAAA,CAAQ,aAAc,EAAA,CAAA;AACxD,MAAI,IAAA,SAAA,CAAU,QAAS,CAAA,QAAQ,CAAG,EAAA;AAChC,QAAO,MAAA,CAAA,KAAA,CAAM,GAAG,QAAmC,CAAA,yBAAA,CAAA,CAAA,CAAA;AACnD,QAAA,GAAA,CAAI,IAAK,CAAA;AAAA,UACP,OAAS,EAAA,IAAA;AAAA,UACT,OAAS,EAAA,gCAAA;AAAA,SACV,CAAA,CAAA;AAAA,OACI,MAAA;AACL,QAAI,GAAA,CAAA,MAAA,CAAO,GAAG,CAAA,CAAE,IAAK,CAAA;AAAA,UACnB,OAAS,EAAA,KAAA;AAAA,UACT,SAAS,CAAa,UAAA,EAAA,QAAA,CAAA,WAAA,CAAA;AAAA,SACvB,CAAA,CAAA;AAAA,OACH;AAAA,KACF;AAAA,GACD,CAAA,CAAA;AAGD,EAAA,MAAA,CAAO,IAAK,CAAA,CAAA,EAAG,kBAA6B,CAAA,OAAA,CAAA,EAAA,OAAO,KAAK,GAAQ,KAAA;AAC9D,IAAM,MAAA,EAAE,QAAS,EAAA,GAAI,GAAI,CAAA,MAAA,CAAA;AACzB,IAAA,MAAM,MAAS,GAAA,MAAM,OAAQ,CAAA,yBAAA,CAA0B,QAAQ,CAAA,CAAA;AAC/D,IAAA,IAAI,MAAQ,EAAA;AACV,MAAM,MAAA,cAAA,GAAiB,IAAI,IAAK,EAAA,CAAA;AAChC,MAAA,cAAA,CAAe,QAAQ,cAAe,CAAA,OAAA,KAAY,EAAK,GAAA,EAAA,GAAK,KAAK,GAAI,CAAA,CAAA;AACrE,MAAM,MAAA,OAAA,CAAQ,aAAa,QAAU,EAAA;AAAA,QACnC,WAAa,EAAA,gBAAA;AAAA,QACb,sBAAA,EAAwB,IAAI,IAAK,EAAA;AAAA,QACjC,cAAA;AAAA,QACA,MAAQ,EAAA,SAAA;AAAA,OACT,CAAA,CAAA;AACD,MAAA,GAAA,CAAI,IAAK,CAAA;AAAA,QACP,OAAS,EAAA,IAAA;AAAA,QACT,SAAS,CAAG,EAAA,QAAA,CAAA,6BAAA,CAAA;AAAA,OACb,CAAA,CAAA;AAAA,KACI,MAAA;AACL,MAAM,MAAA,SAAA,GAAsB,MAAM,OAAA,CAAQ,aAAc,EAAA,CAAA;AACxD,MAAI,IAAA,SAAA,CAAU,QAAS,CAAA,QAAQ,CAAG,EAAA;AAChC,QAAO,MAAA,CAAA,KAAA,CAAM,GAAG,QAAmC,CAAA,yBAAA,CAAA,CAAA,CAAA;AACnD,QAAA,GAAA,CAAI,IAAK,CAAA;AAAA,UACP,OAAS,EAAA,IAAA;AAAA,UACT,OAAS,EAAA,gDAAA;AAAA,SACV,CAAA,CAAA;AAAA,OACI,MAAA;AACL,QAAI,GAAA,CAAA,MAAA,CAAO,GAAG,CAAA,CAAE,IAAK,CAAA;AAAA,UACnB,OAAS,EAAA,KAAA;AAAA,UACT,SAAS,CAAa,UAAA,EAAA,QAAA,CAAA,WAAA,CAAA;AAAA,SACvB,CAAA,CAAA;AAAA,OACH;AAAA,KACF;AAAA,GACD,CAAA,CAAA;AAGD,EAAA,MAAA,CAAO,MAAO,CAAA,kBAAA,EAAoB,OAAO,GAAA,EAAK,GAAQ,KAAA;AACpD,IAAM,MAAA,EAAE,QAAS,EAAA,GAAI,GAAI,CAAA,MAAA,CAAA;AACzB,IAAA,MAAM,MAAS,GAAA,MAAM,OAAQ,CAAA,qBAAA,CAAsB,QAAQ,CAAA,CAAA;AAC3D,IAAA,GAAA,CAAI,KAAK,MAAM,CAAA,CAAA;AAAA,GAChB,CAAA,CAAA;AAGD,EAAA,MAAA,CAAO,GAAI,CAAA,CAAA,EAAG,kBAA4B,CAAA,MAAA,CAAA,EAAA,OAAO,KAAK,GAAQ,KAAA;AAC5D,IAAM,MAAA,EAAE,QAAS,EAAA,GAAI,GAAI,CAAA,MAAA,CAAA;AACzB,IAAA,MAAM,MAAS,GAAA,MAAM,OAAQ,CAAA,yBAAA,CAA0B,QAAQ,CAAA,CAAA;AAC/D,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,MAAM,KAAK,MAAO,CAAA,EAAA,CAAA;AAClB,MAAA,MAAM,OAAU,GAAA,MAAM,OAAQ,CAAA,WAAA,CAAY,EAAE,CAAA,CAAA;AAC5C,MAAA,GAAA,CAAI,IAAK,CAAA,EAAE,OAAS,EAAA,IAAA,EAAM,SAAS,CAAA,CAAA;AAAA,KAC9B,MAAA;AACL,MAAM,MAAA,SAAA,GAAsB,MAAM,OAAA,CAAQ,aAAc,EAAA,CAAA;AACxD,MAAI,IAAA,SAAA,CAAU,QAAS,CAAA,QAAQ,CAAG,EAAA;AAChC,QAAO,MAAA,CAAA,KAAA,CAAM,GAAG,QAAmC,CAAA,yBAAA,CAAA,CAAA,CAAA;AACnD,QAAA,GAAA,CAAI,IAAK,CAAA;AAAA,UACP,OAAS,EAAA,IAAA;AAAA,UACT,OAAS,EAAA,yCAAA;AAAA,SACV,CAAA,CAAA;AAAA,OACI,MAAA;AACL,QAAO,MAAA,CAAA,KAAA;AAAA,UACL,CAAG,EAAA,QAAA,CAAA,6CAAA,CAAA;AAAA,SACL,CAAA;AACA,QAAI,GAAA,CAAA,MAAA,CAAO,GAAG,CAAA,CAAE,IAAK,CAAA;AAAA,UACnB,OAAS,EAAA,KAAA;AAAA,UACT,QAAQ,EAAC;AAAA,UACT,YAAY,CAAa,UAAA,EAAA,QAAA,CAAA,WAAA,CAAA;AAAA,SAC1B,CAAA,CAAA;AAAA,OACH;AAAA,KACF;AAAA,GACD,CAAA,CAAA;AAED,EAAA,MAAA,CAAO,MAAO,CAAA,CAAA,EAAG,kBAA4B,CAAA,MAAA,CAAA,EAAA,OAAO,KAAK,GAAQ,KAAA;AAC/D,IAAM,MAAA,EAAE,QAAS,EAAA,GAAI,GAAI,CAAA,MAAA,CAAA;AACzB,IAAA,MAAM,SAAY,GAAA,MAAM,OAAQ,CAAA,uBAAA,CAAwB,QAAQ,CAAA,CAAA;AAEhE,IAAA,GAAA,CAAI,IAAK,CAAA;AAAA,MACP,OAAS,EAAA,IAAA;AAAA,MACT,SAAS,CAA+B,4BAAA,EAAA,QAAA,CAAA,UAAA,CAAA;AAAA,MACxC,SAAA;AAAA,KACD,CAAA,CAAA;AAAA,GACF,CAAA,CAAA;AAED,EAAO,MAAA,CAAA,GAAA,CAAIC,4BAAc,CAAA,CAAA;AAEzB,EAAO,OAAA,MAAA,CAAA;AACT,CAAA;;;;;;;;;;;;;;;;;;;;ACvOA,IAAA,QAAA,EAAA,OAAA,EAAA,EAAA,CAAA;AAgBO,MAAM,QAAkC,CAAA;AAAA,EAetC,WAAc,GAAA;AAdrB,IAAA,YAAA,CAAA,IAAA,EAAA,QAAA,EAAA,KAAA,CAAA,CAAA,CAAA;AACA,IAAA,YAAA,CAAA,IAAA,EAAA,OAAA,EAAA,KAAA,CAAA,CAAA,CAAA;AAwBA,IAAA,IAAA,CAAC,EAAkC,CAAA,GAAA,UAAA,CAAA;AAVjC,IAAA,MAAM,OAAU,GAAA,IAAI,OAAW,CAAA,CAAC,SAAS,MAAW,KAAA;AAClD,MAAA,YAAA,CAAA,IAAA,EAAK,QAAW,EAAA,OAAA,CAAA,CAAA;AAChB,MAAA,YAAA,CAAA,IAAA,EAAK,OAAU,EAAA,MAAA,CAAA,CAAA;AAAA,KAChB,CAAA,CAAA;AAED,IAAA,IAAA,CAAK,IAAO,GAAA,OAAA,CAAQ,IAAK,CAAA,IAAA,CAAK,OAAO,CAAA,CAAA;AACrC,IAAA,IAAA,CAAK,KAAQ,GAAA,OAAA,CAAQ,KAAM,CAAA,IAAA,CAAK,OAAO,CAAA,CAAA;AACvC,IAAA,IAAA,CAAK,OAAU,GAAA,OAAA,CAAQ,OAAQ,CAAA,IAAA,CAAK,OAAO,CAAA,CAAA;AAAA,GAC7C;AAAA,EApBA,IAAW,OAAU,GAAA;AACnB,IAAA,OAAO,YAAK,CAAA,IAAA,EAAA,QAAA,CAAA,CAAA;AAAA,GACd;AAAA,EACA,IAAW,MAAS,GAAA;AAClB,IAAA,OAAO,YAAK,CAAA,IAAA,EAAA,OAAA,CAAA,CAAA;AAAA,GACd;AAkBF,CAAA;AADG,EAAO,GAAA,MAAA,CAAA,WAAA,CAAA;AAzBR,QAAA,GAAA,IAAA,OAAA,EAAA,CAAA;AACA,OAAA,GAAA,IAAA,OAAA,EAAA;;ACuBK,MAAM,gBAAiB,CAAA;AAAA,EAK5B,YACmB,OAOjB,EAAA;AAPiB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA,CAAA;AAJnB,IAAA,IAAA,CAAQ,0BAA6B,GAAA,CAAA,CAAA;AACrC,IAAiB,IAAA,CAAA,WAAA,GAAc,IAAI,QAAe,EAAA,CAAA;AAAA,GAU/C;AAAA,EAEH,IAAA,CACE,UACA,OACgB,EAAA;AAChB,IAAA,IAAA,CAAK,0BAA8B,IAAA,CAAA,CAAA;AACnC,IAAO,OAAA;AAAA,MACL,eAAA,EAAiB,MAAM,QAAA,CAAS,eAAgB,EAAA;AAAA,MAChD,OAAA,EAAS,OAAM,UAAc,KAAA;AAC3B,QAAA,MAAM,IAAK,CAAA,aAAA,CAAc,QAAU,EAAA,OAAA,EAAS,UAAU,CAAA,CAAA;AACtD,QAAA,IAAA,CAAK,0BAA8B,IAAA,CAAA,CAAA;AACnC,QAAI,IAAA,IAAA,CAAK,+BAA+B,CAAG,EAAA;AACzC,UAAA,IAAA,CAAK,YAAY,OAAQ,EAAA,CAAA;AAAA,SAC3B;AAAA,OACF;AAAA,KACF,CAAA;AAAA,GACF;AAAA,EAEA,MAAM,WAAuC,GAAA;AAC3C,IAAO,OAAA,+BAAA;AAAA,MACL,IAAI,mCAAoC,CAAA,EAAE,QAAQ,IAAK,CAAA,OAAA,CAAQ,QAAQ,CAAA;AAAA,MACvEC,sCAAA,CAAsB,IAAK,CAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,KAC3C,CAAA;AAAA,GACF;AAAA,EAEA,MAAc,aAAA,CACZ,QACA,EAAA,eAAA,EACA,UACA,EAAA;AACA,IAAA,MAAM,MAAS,GAAAA,sCAAA;AAAA,MACb,IAAA,CAAK,OAAQ,CAAA,MAAA,CAAO,KAAM,CAAA;AAAA,QACxB,cAAA,EAAgB,SAAS,eAAgB,EAAA;AAAA,OAC1C,CAAA;AAAA,KACH,CAAA;AAEA,IAAI,IAAA;AACF,MAAI,IAAA,CAAC,KAAK,OAAS,EAAA;AACjB,QAAA,IAAA,CAAK,OAAU,GAAA,OAAA,CAAQ,OAAQ,EAAA,CAAE,KAAK,YAAY;AA7F1D,UAAA,IAAA,EAAA,CAAA;AA8FU,UAAA,MAAM,KACJ,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,OAAQ,CAAA,uBAAA,KAAb,IAAwC,GAAA,EAAA,GAAA,uBAAA,CAAA;AAC1C,UAAM,MAAA,KAAA,CAAM,IAAK,CAAA,OAAA,CAAQ,MAAM,CAAA,CAAA;AAAA,SAChC,CAAA,CAAA;AAAA,OACH;AAEA,MAAA,MAAM,IAAK,CAAA,OAAA,CAAA;AAEX,MAAA,MAAM,EAAE,aAAA,EAAe,WAAa,EAAA,UAAA,EAAe,GAAA,eAAA,CAAA;AAEnD,MAAA,MAAA,CAAO,KAAK,CAAY,UAAA,CAAA,CAAA,CAAA;AAExB,MAAM,MAAA,OAAA,GAAU,IAAI,mCAAoC,CAAA;AAAA,QACtD,MAAA,EAAQ,KAAK,OAAQ,CAAA,MAAA;AAAA,OACtB,CAAA,CAAA;AACD,MAAM,MAAA,MAAA,GAAS,IAAI,0BAA2B,CAAA;AAAA,QAC5C,GAAG,eAAA;AAAA,QACH,OAAO,IAAK,CAAA,WAAA;AAAA,QACZ,OAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,UAAA;AAAA,QACA,UAAA;AAAA,OACD,CAAA,CAAA;AAED,MAAM,MAAA,SAAA,GAAYN,eAAS,UAAW,CAAA,aAAa,IAC/C,aACA,GAAAA,cAAA,CAAS,WAAW,aAAa,CAAA,CAAA;AACrC,MAAM,MAAA,MAAA,GAASA,eAAS,UAAW,CAAA,WAAW,IAC1C,WACA,GAAAA,cAAA,CAAS,WAAW,WAAW,CAAA,CAAA;AAEnC,MAAM,MAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,CAAU,YAAa,CAAA;AAAA,QACxC,EAAA,EAAI,SAAS,eAAgB,EAAA;AAAA,QAC7B,EAAI,EAAA,MAAA,CAAO,MAAO,CAAA,IAAA,CAAK,MAAM,CAAA;AAAA,QAC7B,SAAA;AAAA,QACA,OAAS,EAAA,MAAA;AAAA,OACV,CAAA,CAAA;AAAA,aACM,KAAP,EAAA;AACA,MAAO,MAAA,CAAA,IAAA;AAAA,QACL,CAAA,oDAAA,EAAuD,QAAS,CAAA,eAAA,EAAsB,CAAA,EAAA,EAAAC,qBAAA;AAAA,UACpF,KAAA;AAAA,SACF,CAAA,CAAA;AAAA,OACF,CAAA;AACA,MAAM,MAAA,KAAA,CAAA;AAAA,KACR;AAAA,GACF;AACF;;ACzGO,MAAM,kDACXM,oCAAoB,CAAA;AAAA,EAClB,QAAU,EAAA,SAAA;AAAA,EACV,QAAU,EAAA,oCAAA;AAAA,EACV,QAAA,CACE,KACA,OAMA,EAAA;AACA,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,OAAS,EAAAC,iDAAA;AAAA,QACT,MAAQ,EAAAC,iCAAA;AAAA,QACR,QAAU,EAAAC,mCAAA;AAAA,QACV,UAAY,EAAAC,qCAAA;AAAA,QACZ,MAAQ,EAAAC,iCAAA;AAAA,QACR,SAAW,EAAAC,oCAAA;AAAA,OACb;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,OAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,UAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,OACC,EAAA;AACD,QAAM,MAAA,MAAA,GAAS,MAAM,QAAA,CAAS,SAAU,EAAA,CAAA;AAExC,QAAM,MAAA,SAAA,GAAY,IAAI,gBAAiB,CAAA;AAAA,UACrC,MAAA;AAAA,UACA,MAAA;AAAA,UACA,MAAA;AAAA,UACA,SAAA;AAAA,SACD,CAAA,CAAA;AAED,QAAW,KAAA,MAAA,KAAA,IAAS,QAAQ,SAAW,EAAA;AACrC,UAAA,MAAM,UAAU,SAAU,CAAA,IAAA,CAAK,KAAM,CAAA,QAAA,EAAU,MAAM,OAAO,CAAA,CAAA;AAC5D,UAAA,OAAA,CAAQ,kBAAkB,OAAO,CAAA,CAAA;AAAA,SACnC;AAEA,QAAA,UAAA,CAAW,GAAI,CAAA,MAAM,SAAU,CAAA,WAAA,EAAa,CAAA,CAAA;AAAA,OAC9C;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;ACtDI,MAAM,yBAA0B,CAAA;AAAA,EAe7B,WACE,CAAA,GAAA,EACA,OACA,EAAA,MAAA,EACA,OACR,EAAA;AAJQ,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA,CAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA,CAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA,CAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA,CAAA;AAER,IAAK,IAAA,CAAA,KAAA,GAAQ,IAAI,QAAe,EAAA,CAAA;AAAA,GAClC;AAAA,EAfA,aAAa,MAAO,CAAA,GAAA,EAAwB,OAA6B,EAAA;AACvE,IAAA,MAAM,MAAS,GAAA,MAAM,GAAI,CAAA,QAAA,CAAS,SAAU,EAAA,CAAA;AAC5C,IAAA,MAAM,OAAU,GAAA,IAAI,mCAAoC,CAAA,EAAE,QAAQ,CAAA,CAAA;AAClE,IAAA,OAAO,IAAI,yBAAA,CAA0B,GAAK,EAAA,OAAA,EAAS,QAAQ,OAAO,CAAA,CAAA;AAAA,GACpE;AAAA,EAaA,MAAM,KAAQ,GAAA;AACZ,IAAM,MAAA,uBAAA,CAAwB,KAAK,MAAM,CAAA,CAAA;AACzC,IAAA,IAAA,CAAK,MAAM,OAAQ,EAAA,CAAA;AAEnB,IAAA,MAAM,YAAe,GAAA,IAAA,CAAK,GAAI,CAAA,MAAA,CAAO,KAAM,CAAA;AAAA,MACzC,MAAQ,EAAA,0BAAA;AAAA,KACT,CAAA,CAAA;AAED,IAAA,MAAM,yBAAyB,MAAM,+BAAA;AAAA,MACnC,IAAK,CAAA,OAAA;AAAA,MACL,YAAA;AAAA,KACF,CAAA;AAEA,IAAA,OAAO,EAAE,sBAAuB,EAAA,CAAA;AAAA,GAClC;AAAA,EAEA,4BAAA,CACE,UACA,OACA,EAAA;AACA,IAAA,MAAM,EAAE,aAAA,EAAe,WAAa,EAAA,UAAA,EAAe,GAAA,OAAA,CAAA;AACnD,IAAA,MAAM,EAAE,MAAA,EAAQ,aAAe,EAAA,SAAA,KAAc,IAAK,CAAA,GAAA,CAAA;AAClD,IAAA,MAAM,QAAQ,IAAK,CAAA,KAAA,CAAA;AAEnB,IAAA,MAAM,UAAU,IAAK,CAAA,OAAA,CAAA;AAErB,IAAA,IAAA,CAAK,QAAQ,iBAAkB,CAAA;AAAA,MAC7B,eAAiB,EAAA,QAAA,CAAS,eAAgB,CAAA,IAAA,CAAK,QAAQ,CAAA;AAAA,MACvD,MAAM,QAAQ,UAAY,EAAA;AACxB,QAAM,MAAA,MAAA,GAAS,cAAc,KAAM,CAAA;AAAA,UACjC,cAAA,EAAgB,SAAS,eAAgB,EAAA;AAAA,SAC1C,CAAA,CAAA;AAED,QAAA,MAAA,CAAO,KAAK,CAAY,UAAA,CAAA,CAAA,CAAA;AAExB,QAAM,MAAA,MAAA,GAAS,IAAI,0BAA2B,CAAA;AAAA,UAC5C,GAAG,OAAA;AAAA,UACH,KAAA;AAAA,UACA,OAAA;AAAA,UACA,MAAA;AAAA,UACA,QAAA;AAAA,UACA,UAAA;AAAA,UACA,UAAA;AAAA,SACD,CAAA,CAAA;AAED,QAAM,MAAA,SAAA,GAAYb,eAAS,UAAW,CAAA,aAAa,IAC/C,aACA,GAAAA,cAAA,CAAS,WAAW,aAAa,CAAA,CAAA;AACrC,QAAM,MAAA,MAAA,GAASA,eAAS,UAAW,CAAA,WAAW,IAC1C,WACA,GAAAA,cAAA,CAAS,WAAW,WAAW,CAAA,CAAA;AAEnC,QAAA,MAAM,UAAU,YAAa,CAAA;AAAA,UAC3B,EAAA,EAAI,SAAS,eAAgB,EAAA;AAAA,UAC7B,EAAI,EAAA,MAAA,CAAO,MAAO,CAAA,IAAA,CAAK,MAAM,CAAA;AAAA,UAC7B,SAAA;AAAA,UACA,OAAS,EAAA,MAAA;AAAA,SACV,CAAA,CAAA;AAAA,OACH;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-catalog-backend-module-incremental-ingestion",
|
|
3
3
|
"description": "An entity provider for streaming large asset sources into the catalog",
|
|
4
|
-
"version": "0.0.0
|
|
4
|
+
"version": "0.1.0-next.0",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
"postpack": "backstage-cli package postpack"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@backstage/backend-common": "^0.
|
|
37
|
-
"@backstage/backend-plugin-api": "^0.
|
|
38
|
-
"@backstage/backend-tasks": "^0.
|
|
39
|
-
"@backstage/backend-test-utils": "^0.
|
|
40
|
-
"@backstage/catalog-model": "^
|
|
41
|
-
"@backstage/config": "^
|
|
42
|
-
"@backstage/errors": "^
|
|
43
|
-
"@backstage/plugin-catalog-backend": "^
|
|
44
|
-
"@backstage/plugin-catalog-node": "^
|
|
45
|
-
"@backstage/plugin-permission-common": "^0.
|
|
36
|
+
"@backstage/backend-common": "^0.17.0-next.1",
|
|
37
|
+
"@backstage/backend-plugin-api": "^0.1.5-next.1",
|
|
38
|
+
"@backstage/backend-tasks": "^0.4.0-next.1",
|
|
39
|
+
"@backstage/backend-test-utils": "^0.1.31-next.1",
|
|
40
|
+
"@backstage/catalog-model": "^1.1.4-next.1",
|
|
41
|
+
"@backstage/config": "^1.0.5-next.1",
|
|
42
|
+
"@backstage/errors": "^1.1.4-next.1",
|
|
43
|
+
"@backstage/plugin-catalog-backend": "^1.6.0-next.1",
|
|
44
|
+
"@backstage/plugin-catalog-node": "^1.2.2-next.1",
|
|
45
|
+
"@backstage/plugin-permission-common": "^0.7.2-next.1",
|
|
46
46
|
"@types/express": "^4.17.6",
|
|
47
47
|
"@types/luxon": "^3.0.0",
|
|
48
48
|
"express": "^4.17.1",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"winston": "^3.2.1"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@backstage/backend-app-api": "^0.
|
|
58
|
-
"@backstage/cli": "^0.
|
|
59
|
-
"@backstage/plugin-catalog-backend": "^
|
|
57
|
+
"@backstage/backend-app-api": "^0.2.4-next.1",
|
|
58
|
+
"@backstage/cli": "^0.21.2-next.1",
|
|
59
|
+
"@backstage/plugin-catalog-backend": "^1.6.0-next.1",
|
|
60
60
|
"get-port": "^6.1.2"
|
|
61
61
|
},
|
|
62
62
|
"files": [
|