@backstage/plugin-scaffolder-backend 3.4.1-next.0 → 4.0.0-next.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"DatabaseTaskStore.cjs.js","sources":["../../../src/scaffolder/tasks/DatabaseTaskStore.ts"],"sourcesContent":["/*\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\nimport { JsonObject } from '@backstage/types';\nimport {\n DatabaseService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport {\n TaskStore,\n TaskStoreCreateTaskOptions,\n TaskStoreCreateTaskResult,\n TaskStoreEmitOptions,\n TaskStoreListEventsOptions,\n TaskStoreRecoverTaskOptions,\n TaskStoreShutDownTaskOptions,\n} from './types';\nimport {\n SerializedTask,\n SerializedTaskEvent,\n TaskEventType,\n TaskFilter,\n TaskSecrets,\n TaskStatus,\n} from '@backstage/plugin-scaffolder-node';\nimport { DateTime, Duration } from 'luxon';\nimport { TaskRecovery, TaskSpec } from '@backstage/plugin-scaffolder-common';\nimport { trimEventsTillLastRecovery } from './taskRecoveryHelper';\nimport { intervalFromNowTill } from './dbUtil';\nimport {\n restoreWorkspace,\n serializeWorkspace,\n} from '@backstage/plugin-scaffolder-node/alpha';\nimport { flattenParams } from '../../service/helpers';\nimport { EventsService } from '@backstage/plugin-events-node';\nimport { PermissionCriteria } from '@backstage/plugin-permission-common';\nimport {\n isAndCriteria,\n isNotCriteria,\n isOrCriteria,\n} from '@backstage/plugin-permission-node';\nimport { TaskFilters } from '@backstage/plugin-scaffolder-node';\nimport { compact } from 'lodash';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-scaffolder-backend',\n 'migrations',\n);\n\nexport type RawDbTaskRow = {\n id: string;\n spec: string;\n status: TaskStatus;\n state?: string;\n last_heartbeat_at?: string;\n created_at: string;\n created_by: string | null;\n secrets?: string | null;\n workspace?: Buffer;\n};\n\nexport type RawDbTaskEventRow = {\n id: number;\n task_id: string;\n body: string;\n event_type: TaskEventType;\n created_at: string;\n};\n\n/**\n * DatabaseTaskStore\n */\nexport type DatabaseTaskStoreOptions = {\n database: DatabaseService | Knex;\n events?: EventsService;\n};\n\n/**\n * Type guard to help DatabaseTaskStore understand when database is DatabaseService vs. when database is a Knex instance.\n * */\nfunction isDatabaseService(\n opt: DatabaseService | Knex,\n): opt is DatabaseService {\n return (opt as DatabaseService).getClient !== undefined;\n}\n\nconst parseSqlDateToIsoString = <T>(input: T): T | string => {\n if (typeof input === 'string') {\n const parsed = DateTime.fromSQL(input, { zone: 'UTC' });\n if (!parsed.isValid) {\n throw new Error(\n `Failed to parse database timestamp '${input}', ${parsed.invalidReason}: ${parsed.invalidExplanation}`,\n );\n }\n return parsed.toISO()!;\n }\n\n return input;\n};\n\n/**\n * DatabaseTaskStore\n */\nexport class DatabaseTaskStore implements TaskStore {\n private readonly db: Knex;\n private readonly events?: EventsService;\n\n static async create(\n options: DatabaseTaskStoreOptions,\n ): Promise<DatabaseTaskStore> {\n const { database } = options;\n const client = await this.getClient(database);\n\n await this.runMigrations(database, client);\n\n return new DatabaseTaskStore(client, options.events);\n }\n\n private isRecoverableTask(spec: TaskSpec): boolean {\n return ['startOver'].includes(\n spec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? 'none',\n );\n }\n\n private parseSpec({ spec, id }: { spec: string; id: string }): TaskSpec {\n try {\n return JSON.parse(spec);\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${id}', ${error}`);\n }\n }\n\n private parseTaskSecrets(taskRow: RawDbTaskRow): TaskSecrets | undefined {\n try {\n return taskRow.secrets ? JSON.parse(taskRow.secrets) : undefined;\n } catch (error) {\n throw new Error(\n `Failed to parse secrets of task '${taskRow.id}', ${error}`,\n );\n }\n }\n\n private static async getClient(\n database: DatabaseService | Knex,\n ): Promise<Knex> {\n if (isDatabaseService(database)) {\n return database.getClient();\n }\n\n return database;\n }\n\n private static async runMigrations(\n database: DatabaseService | Knex,\n client: Knex,\n ): Promise<void> {\n if (!isDatabaseService(database)) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n\n return;\n }\n\n if (!database.migrations?.skip) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n }\n\n private constructor(client: Knex, events?: EventsService) {\n this.db = client;\n this.events = events;\n }\n\n private getState(task: RawDbTaskRow) {\n try {\n return task.state ? JSON.parse(task.state).state : undefined;\n } catch (error) {\n throw new Error(\n `Failed to parse state of the task '${task.id}', ${error}`,\n );\n }\n }\n\n private isTaskFilter(filter: any): filter is TaskFilter {\n return filter.hasOwnProperty('key');\n }\n\n private parseFilter(\n filter: PermissionCriteria<TaskFilters>,\n query: Knex.QueryBuilder,\n db: Knex,\n negate: boolean = false,\n ): Knex.QueryBuilder {\n if (isNotCriteria(filter)) {\n return this.parseFilter(filter.not, query, db, !negate);\n }\n\n if (this.isTaskFilter(filter)) {\n const values: string[] = compact(filter.values) ?? [];\n if (negate) {\n query.whereNotIn(filter.key, values);\n } else {\n query.whereIn(filter.key, values);\n }\n\n return query;\n }\n\n return query[negate ? 'andWhereNot' : 'andWhere'](subQuery => {\n if (isOrCriteria(filter)) {\n for (const subFilter of filter.anyOf ?? []) {\n subQuery.orWhere(subQueryInner =>\n this.parseFilter(subFilter, subQueryInner, db, false),\n );\n }\n } else if (isAndCriteria(filter)) {\n for (const subFilter of filter.allOf ?? []) {\n subQuery.andWhere(subQueryInner =>\n this.parseFilter(subFilter, subQueryInner, db, false),\n );\n }\n }\n });\n }\n\n async list(options: {\n createdBy?: string;\n status?: TaskStatus;\n filters?: {\n createdBy?: string | string[];\n status?: TaskStatus | TaskStatus[];\n };\n pagination?: {\n limit?: number;\n offset?: number;\n };\n order?: { order: 'asc' | 'desc'; field: string }[];\n permissionFilters?: PermissionCriteria<TaskFilters>;\n }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }> {\n const { createdBy, status, pagination, order, filters, permissionFilters } =\n options ?? {};\n const queryBuilder = this.db<RawDbTaskRow & { count: number }>('tasks');\n\n const createdByValues = flattenParams<string>(\n createdBy,\n filters?.createdBy,\n );\n\n const combinedPermissionFilters:\n | PermissionCriteria<TaskFilters>\n | undefined =\n createdByValues.length > 0\n ? {\n allOf: [\n { key: 'created_by', values: createdByValues },\n ...(permissionFilters ? [permissionFilters] : []),\n ],\n }\n : permissionFilters;\n\n if (combinedPermissionFilters) {\n this.parseFilter(combinedPermissionFilters, queryBuilder, this.db);\n }\n\n if (status || filters?.status) {\n const arr: TaskStatus[] = flattenParams<TaskStatus>(\n status,\n filters?.status,\n );\n queryBuilder.whereIn('status', [...new Set(arr)]);\n }\n\n const countQuery = queryBuilder.clone();\n countQuery.count('tasks.id', { as: 'count' });\n\n if (order) {\n order.forEach(f => {\n queryBuilder.orderBy(f.field, f.order);\n });\n } else {\n queryBuilder.orderBy('created_at', 'desc');\n }\n\n if (pagination?.limit !== undefined) {\n queryBuilder.limit(pagination.limit);\n }\n\n if (pagination?.offset !== undefined) {\n queryBuilder.offset(pagination.offset);\n }\n\n const [results, [{ count }]] = await Promise.all([\n queryBuilder.select(),\n countQuery,\n ]);\n\n const tasks = results.map(result => ({\n id: result.id,\n spec: JSON.parse(result.spec),\n status: result.status,\n createdBy: result.created_by ?? undefined,\n lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),\n createdAt: parseSqlDateToIsoString(result.created_at),\n }));\n\n return { tasks, totalTasks: count };\n }\n\n async getTask(taskId: string): Promise<SerializedTask> {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId })\n .select();\n if (!result) {\n throw new NotFoundError(`No task with id '${taskId}' found`);\n }\n try {\n return this.parseTaskRow(result);\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${taskId}', ${error}`);\n }\n }\n\n private parseTaskRow(result: RawDbTaskRow): SerializedTask {\n const spec = JSON.parse(result.spec);\n const secrets = result.secrets ? JSON.parse(result.secrets) : undefined;\n const state = this.getState(result);\n\n return {\n id: result.id,\n spec,\n status: result.status,\n lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),\n createdAt: parseSqlDateToIsoString(result.created_at),\n createdBy: result.created_by ?? undefined,\n secrets,\n state,\n };\n }\n\n async createTask(\n options: TaskStoreCreateTaskOptions,\n ): Promise<TaskStoreCreateTaskResult> {\n const taskId = uuid();\n await this.db<RawDbTaskRow>('tasks').insert({\n id: taskId,\n spec: JSON.stringify(options.spec),\n secrets: options.secrets ? JSON.stringify(options.secrets) : undefined,\n created_by: options.createdBy ?? null,\n status: 'open',\n });\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: {\n id: taskId,\n spec: options.spec,\n createdBy: options.createdBy,\n status: 'open',\n },\n });\n\n return { taskId };\n }\n\n async claimTask(): Promise<SerializedTask | undefined> {\n return this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n status: 'open',\n })\n .limit(1)\n .select();\n\n if (!task) {\n return undefined;\n }\n\n const spec = this.parseSpec(task);\n\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where({ id: task.id, status: 'open' })\n .update({\n status: 'processing',\n last_heartbeat_at: this.db.fn.now(),\n // remove the secrets for non-recoverable tasks when moving to processing state.\n secrets: this.isRecoverableTask(spec) ? task.secrets : null,\n });\n\n if (updateCount < 1) {\n return undefined;\n }\n\n const ret: SerializedTask = {\n id: task.id,\n spec,\n status: 'processing',\n lastHeartbeatAt: task.last_heartbeat_at,\n createdAt: task.created_at,\n createdBy: task.created_by ?? undefined,\n state: this.getState(task),\n };\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: ret,\n });\n\n const secrets = this.parseTaskSecrets(task);\n return { ...ret, secrets };\n });\n }\n\n async heartbeatTask(taskId: string): Promise<void> {\n const updateCount = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId, status: 'processing' })\n .update({\n last_heartbeat_at: this.db.fn.now(),\n });\n if (updateCount === 0) {\n throw new ConflictError(`No running task with taskId ${taskId} found`);\n }\n }\n\n async listStaleTasks(options: { timeoutS: number }): Promise<{\n tasks: { taskId: string; recovery?: TaskRecovery }[];\n }> {\n const { timeoutS } = options;\n const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);\n const rawRows = await this.db<RawDbTaskRow>('tasks')\n .where('status', 'processing')\n .andWhere('last_heartbeat_at', '<=', heartbeatInterval);\n const tasks = rawRows.map(row => ({\n recovery: (JSON.parse(row.spec) as TaskSpec).EXPERIMENTAL_recovery,\n taskId: row.id,\n }));\n return { tasks };\n }\n\n async completeTask(options: {\n taskId: string;\n status: TaskStatus;\n eventBody: JsonObject;\n }): Promise<void> {\n const { taskId, status, eventBody } = options;\n\n let oldStatus: TaskStatus;\n if (['failed', 'completed', 'cancelled'].includes(status)) {\n oldStatus = 'processing';\n } else {\n throw new Error(\n `Invalid status update of run '${taskId}' to status '${status}'`,\n );\n }\n\n await this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n id: taskId,\n })\n .limit(1)\n .select();\n\n const updateTask = async (criteria: {\n id: string;\n status?: TaskStatus;\n }) => {\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where(criteria)\n .update({\n status,\n secrets: null,\n });\n\n if (updateCount !== 1) {\n throw new ConflictError(\n `Failed to update status to '${status}' for taskId ${taskId}`,\n );\n }\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: {\n id: taskId,\n status: status,\n lastHeartbeatAt: task.last_heartbeat_at,\n createdAt: task.created_at,\n createdBy: task.created_by,\n state: this.getState(task),\n },\n });\n\n await tx<RawDbTaskEventRow>('task_events')\n .insert({\n task_id: taskId,\n event_type: 'completion',\n body: JSON.stringify(eventBody),\n })\n .returning('id');\n };\n\n if (status === 'cancelled') {\n await updateTask({\n id: taskId,\n });\n return;\n }\n\n if (task.status === 'cancelled') {\n return;\n }\n\n if (!task) {\n throw new Error(`No task with taskId ${taskId} found`);\n }\n if (task.status !== oldStatus) {\n throw new ConflictError(\n `Refusing to update status of run '${taskId}' to status '${status}' ` +\n `as it is currently '${task.status}', expected '${oldStatus}'`,\n );\n }\n\n await updateTask({\n id: taskId,\n status: oldStatus,\n });\n });\n }\n\n async emitLogEvent(\n options: TaskStoreEmitOptions<{ message: string } & JsonObject>,\n ): Promise<void> {\n const { taskId, body } = options;\n const serializedBody = JSON.stringify(body);\n await this.db<RawDbTaskEventRow>('task_events')\n .insert({\n task_id: taskId,\n event_type: 'log',\n body: serializedBody,\n })\n .returning('id');\n }\n\n async getTaskState({ taskId }: { taskId: string }): Promise<\n | {\n state: JsonObject;\n }\n | undefined\n > {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId })\n .select('state');\n return result.state ? JSON.parse(result.state) : undefined;\n }\n\n async saveTaskState(options: {\n taskId: string;\n state?: JsonObject;\n }): Promise<void> {\n if (options.state) {\n const serializedState = JSON.stringify({ state: options.state });\n await this.db<RawDbTaskRow>('tasks')\n .where({ id: options.taskId })\n .update({\n state: serializedState,\n });\n }\n }\n\n async listEvents(\n options: TaskStoreListEventsOptions,\n ): Promise<{ events: SerializedTaskEvent[] }> {\n const { isTaskRecoverable, taskId, after } = options;\n const rawEvents = await this.db<RawDbTaskEventRow>('task_events')\n .where({\n task_id: taskId,\n })\n .andWhere(builder => {\n if (typeof after === 'number') {\n builder.where('id', '>', after).orWhere('event_type', 'completion');\n }\n })\n .orderBy('id')\n .select();\n\n const events = rawEvents.map(event => {\n try {\n const body = JSON.parse(event.body) as SerializedTaskEvent['body'];\n return {\n id: Number(event.id),\n isTaskRecoverable,\n taskId,\n body,\n type: event.event_type,\n createdAt: parseSqlDateToIsoString(event.created_at),\n };\n } catch (error) {\n throw new Error(\n `Failed to parse event body from event taskId=${taskId} id=${event.id}, ${error}`,\n );\n }\n });\n\n return trimEventsTillLastRecovery(events);\n }\n\n async shutdownTask(options: TaskStoreShutDownTaskOptions): Promise<void> {\n const { taskId } = options;\n const message = `This task was marked as stale as it exceeded its timeout`;\n\n const statusStepEvents = (await this.listEvents({ taskId })).events.filter(\n ({ body }) => body?.stepId,\n );\n\n const completedSteps = statusStepEvents\n .filter(\n ({ body: { status } }) => status === 'failed' || status === 'completed',\n )\n .map(step => step.body.stepId);\n\n const hungProcessingSteps = statusStepEvents\n .filter(({ body: { status } }) => status === 'processing')\n .map(event => event.body.stepId)\n .filter(step => !completedSteps.includes(step));\n\n for (const step of hungProcessingSteps) {\n await this.emitLogEvent({\n taskId,\n body: {\n message,\n stepId: step,\n status: 'failed',\n },\n });\n }\n\n await this.completeTask({\n taskId,\n status: 'failed',\n eventBody: {\n message,\n },\n });\n }\n\n async rehydrateWorkspace(options: {\n taskId: string;\n targetPath: string;\n }): Promise<void> {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: options.taskId })\n .select('workspace');\n\n await restoreWorkspace({\n path: options.targetPath,\n buffer: result.workspace,\n });\n }\n\n async cleanWorkspace({ taskId }: { taskId: string }): Promise<void> {\n await this.db('tasks').where({ id: taskId }).update({\n workspace: null,\n });\n }\n\n async serializeWorkspace(options: {\n path: string;\n taskId: string;\n }): Promise<void> {\n if (options.path) {\n const workspace = (await serializeWorkspace(options)).contents;\n await this.db<RawDbTaskRow>('tasks')\n .where({ id: options.taskId })\n .update({\n workspace,\n });\n }\n }\n\n async cancelTask(\n options: TaskStoreEmitOptions<{ message: string } & JsonObject>,\n ): Promise<void> {\n const { taskId, body } = options;\n const serializedBody = JSON.stringify(body);\n const [ret] = await this.db<RawDbTaskEventRow>('task_events')\n .insert({\n task_id: taskId,\n event_type: 'cancelled',\n body: serializedBody,\n })\n .returning('id');\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: {\n id: ret.id,\n taskId,\n status: 'cancelled',\n body,\n },\n });\n }\n\n async retryTask(options: {\n secrets?: TaskSecrets;\n taskId: string;\n }): Promise<void> {\n const { secrets, taskId } = options;\n\n await this.db.transaction(async tx => {\n const result = await tx<RawDbTaskRow>('tasks')\n .where('id', taskId)\n .update(\n {\n ...(secrets && { secrets: JSON.stringify(secrets) }),\n status: 'open',\n last_heartbeat_at: this.db.fn.now(),\n },\n ['id', 'spec'],\n );\n\n for (const { id, spec } of result) {\n const taskSpec = JSON.parse(spec as string) as TaskSpec;\n\n /**\n * Once task is picked up, all event types are replayed.\n * We have to remove cancelled or completion event_type as these are as actions for frontend to perform.\n * In contrary, we send 'recovered' event_type to reset the state on the frontend side.\n *\n */\n await tx<RawDbTaskEventRow>('task_events')\n .where('task_id', id)\n .andWhere(q => q.whereIn('event_type', ['cancelled', 'completion']))\n .del();\n\n await tx<RawDbTaskEventRow>('task_events').insert({\n task_id: id,\n event_type: 'recovered',\n body: JSON.stringify({\n recoverStrategy:\n taskSpec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? 'none',\n }),\n });\n }\n });\n }\n\n async recoverTasks(\n options: TaskStoreRecoverTaskOptions,\n ): Promise<{ ids: string[] }> {\n const taskIdsToRecover: string[] = [];\n const timeoutS = Duration.fromObject(options.timeout).as('seconds');\n\n await this.db.transaction(async tx => {\n const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);\n\n const result = await tx<RawDbTaskRow>('tasks')\n .where('status', 'processing')\n .andWhere('last_heartbeat_at', '<=', heartbeatInterval)\n .update(\n {\n status: 'open',\n last_heartbeat_at: this.db.fn.now(),\n },\n ['id', 'spec'],\n );\n\n taskIdsToRecover.push(...result.map(i => i.id));\n\n for (const { id, spec } of result) {\n const taskSpec = JSON.parse(spec as string) as TaskSpec;\n const event = {\n recoverStrategy:\n taskSpec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? 'none',\n };\n const [ret] = await tx<RawDbTaskEventRow>('task_events')\n .insert({\n task_id: id,\n event_type: 'recovered',\n body: JSON.stringify(event),\n })\n .returning('id');\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: {\n id: ret.id,\n taskId: id,\n status: 'recovered',\n body: event,\n },\n });\n }\n });\n\n return { ids: taskIdsToRecover };\n }\n}\n"],"names":["resolvePackagePath","DateTime","isNotCriteria","compact","isOrCriteria","isAndCriteria","flattenParams","NotFoundError","uuid","ConflictError","intervalFromNowTill","trimEventsTillLastRecovery","restoreWorkspace","serializeWorkspace","Duration"],"mappings":";;;;;;;;;;;;;AA4DA,MAAM,aAAA,GAAgBA,mCAAA;AAAA,EACpB,sCAAA;AAAA,EACA;AACF,CAAA;AAiCA,SAAS,kBACP,GAAA,EACwB;AACxB,EAAA,OAAQ,IAAwB,SAAA,KAAc,MAAA;AAChD;AAEA,MAAM,uBAAA,GAA0B,CAAI,KAAA,KAAyB;AAC3D,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,MAAM,SAASC,cAAA,CAAS,OAAA,CAAQ,OAAO,EAAE,IAAA,EAAM,OAAO,CAAA;AACtD,IAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,uCAAuC,KAAK,CAAA,GAAA,EAAM,OAAO,aAAa,CAAA,EAAA,EAAK,OAAO,kBAAkB,CAAA;AAAA,OACtG;AAAA,IACF;AACA,IAAA,OAAO,OAAO,KAAA,EAAM;AAAA,EACtB;AAEA,EAAA,OAAO,KAAA;AACT,CAAA;AAKO,MAAM,iBAAA,CAAuC;AAAA,EACjC,EAAA;AAAA,EACA,MAAA;AAAA,EAEjB,aAAa,OACX,OAAA,EAC4B;AAC5B,IAAA,MAAM,EAAE,UAAS,GAAI,OAAA;AACrB,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAA;AAE5C,IAAA,MAAM,IAAA,CAAK,aAAA,CAAc,QAAA,EAAU,MAAM,CAAA;AAEzC,IAAA,OAAO,IAAI,iBAAA,CAAkB,MAAA,EAAQ,OAAA,CAAQ,MAAM,CAAA;AAAA,EACrD;AAAA,EAEQ,kBAAkB,IAAA,EAAyB;AACjD,IAAA,OAAO,CAAC,WAAW,CAAA,CAAE,QAAA;AAAA,MACnB,IAAA,CAAK,uBAAuB,qBAAA,IAAyB;AAAA,KACvD;AAAA,EACF;AAAA,EAEQ,SAAA,CAAU,EAAE,IAAA,EAAM,EAAA,EAAG,EAA2C;AACtE,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,IACxB,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,8BAAA,EAAiC,EAAE,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,CAAA;AAAA,IAClE;AAAA,EACF;AAAA,EAEQ,iBAAiB,OAAA,EAAgD;AACvE,IAAA,IAAI;AACF,MAAA,OAAO,QAAQ,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,KAAA,CAAA;AAAA,IACzD,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,iCAAA,EAAoC,OAAA,CAAQ,EAAE,CAAA,GAAA,EAAM,KAAK,CAAA;AAAA,OAC3D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,aAAqB,UACnB,QAAA,EACe;AACf,IAAA,IAAI,iBAAA,CAAkB,QAAQ,CAAA,EAAG;AAC/B,MAAA,OAAO,SAAS,SAAA,EAAU;AAAA,IAC5B;AAEA,IAAA,OAAO,QAAA;AAAA,EACT;AAAA,EAEA,aAAqB,aAAA,CACnB,QAAA,EACA,MAAA,EACe;AACf,IAAA,IAAI,CAAC,iBAAA,CAAkB,QAAQ,CAAA,EAAG;AAChC,MAAA,MAAM,MAAA,CAAO,QAAQ,MAAA,CAAO;AAAA,QAC1B,SAAA,EAAW;AAAA,OACZ,CAAA;AAED,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,QAAA,CAAS,UAAA,EAAY,IAAA,EAAM;AAC9B,MAAA,MAAM,MAAA,CAAO,QAAQ,MAAA,CAAO;AAAA,QAC1B,SAAA,EAAW;AAAA,OACZ,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,WAAA,CAAY,QAAc,MAAA,EAAwB;AACxD,IAAA,IAAA,CAAK,EAAA,GAAK,MAAA;AACV,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAEQ,SAAS,IAAA,EAAoB;AACnC,IAAA,IAAI;AACF,MAAA,OAAO,KAAK,KAAA,GAAQ,IAAA,CAAK,MAAM,IAAA,CAAK,KAAK,EAAE,KAAA,GAAQ,KAAA,CAAA;AAAA,IACrD,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,mCAAA,EAAsC,IAAA,CAAK,EAAE,CAAA,GAAA,EAAM,KAAK,CAAA;AAAA,OAC1D;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,aAAa,MAAA,EAAmC;AACtD,IAAA,OAAO,MAAA,CAAO,eAAe,KAAK,CAAA;AAAA,EACpC;AAAA,EAEQ,WAAA,CACN,MAAA,EACA,KAAA,EACA,EAAA,EACA,SAAkB,KAAA,EACC;AACnB,IAAA,IAAIC,kCAAA,CAAc,MAAM,CAAA,EAAG;AACzB,MAAA,OAAO,KAAK,WAAA,CAAY,MAAA,CAAO,KAAK,KAAA,EAAO,EAAA,EAAI,CAAC,MAAM,CAAA;AAAA,IACxD;AAEA,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,MAAM,CAAA,EAAG;AAC7B,MAAA,MAAM,MAAA,GAAmBC,cAAA,CAAQ,MAAA,CAAO,MAAM,KAAK,EAAC;AACpD,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,KAAA,CAAM,UAAA,CAAW,MAAA,CAAO,GAAA,EAAK,MAAM,CAAA;AAAA,MACrC,CAAA,MAAO;AACL,QAAA,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,GAAA,EAAK,MAAM,CAAA;AAAA,MAClC;AAEA,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,OAAO,KAAA,CAAM,MAAA,GAAS,aAAA,GAAgB,UAAU,EAAE,CAAA,QAAA,KAAY;AAC5D,MAAA,IAAIC,iCAAA,CAAa,MAAM,CAAA,EAAG;AACxB,QAAA,KAAA,MAAW,SAAA,IAAa,MAAA,CAAO,KAAA,IAAS,EAAC,EAAG;AAC1C,UAAA,QAAA,CAAS,OAAA;AAAA,YAAQ,mBACf,IAAA,CAAK,WAAA,CAAY,SAAA,EAAW,aAAA,EAAe,IAAI,KAAK;AAAA,WACtD;AAAA,QACF;AAAA,MACF,CAAA,MAAA,IAAWC,kCAAA,CAAc,MAAM,CAAA,EAAG;AAChC,QAAA,KAAA,MAAW,SAAA,IAAa,MAAA,CAAO,KAAA,IAAS,EAAC,EAAG;AAC1C,UAAA,QAAA,CAAS,QAAA;AAAA,YAAS,mBAChB,IAAA,CAAK,WAAA,CAAY,SAAA,EAAW,aAAA,EAAe,IAAI,KAAK;AAAA,WACtD;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,KAAK,OAAA,EAamD;AAC5D,IAAA,MAAM,EAAE,WAAW,MAAA,EAAQ,UAAA,EAAY,OAAO,OAAA,EAAS,iBAAA,EAAkB,GACvE,OAAA,IAAW,EAAC;AACd,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,EAAA,CAAqC,OAAO,CAAA;AAEtE,IAAA,MAAM,eAAA,GAAkBC,qBAAA;AAAA,MACtB,SAAA;AAAA,MACA,OAAA,EAAS;AAAA,KACX;AAEA,IAAA,MAAM,yBAAA,GAGJ,eAAA,CAAgB,MAAA,GAAS,CAAA,GACrB;AAAA,MACE,KAAA,EAAO;AAAA,QACL,EAAE,GAAA,EAAK,YAAA,EAAc,MAAA,EAAQ,eAAA,EAAgB;AAAA,QAC7C,GAAI,iBAAA,GAAoB,CAAC,iBAAiB,IAAI;AAAC;AACjD,KACF,GACA,iBAAA;AAEN,IAAA,IAAI,yBAAA,EAA2B;AAC7B,MAAA,IAAA,CAAK,WAAA,CAAY,yBAAA,EAA2B,YAAA,EAAc,IAAA,CAAK,EAAE,CAAA;AAAA,IACnE;AAEA,IAAA,IAAI,MAAA,IAAU,SAAS,MAAA,EAAQ;AAC7B,MAAA,MAAM,GAAA,GAAoBA,qBAAA;AAAA,QACxB,MAAA;AAAA,QACA,OAAA,EAAS;AAAA,OACX;AACA,MAAA,YAAA,CAAa,OAAA,CAAQ,UAAU,CAAC,GAAG,IAAI,GAAA,CAAI,GAAG,CAAC,CAAC,CAAA;AAAA,IAClD;AAEA,IAAA,MAAM,UAAA,GAAa,aAAa,KAAA,EAAM;AACtC,IAAA,UAAA,CAAW,KAAA,CAAM,UAAA,EAAY,EAAE,EAAA,EAAI,SAAS,CAAA;AAE5C,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,KAAA,CAAM,QAAQ,CAAA,CAAA,KAAK;AACjB,QAAA,YAAA,CAAa,OAAA,CAAQ,CAAA,CAAE,KAAA,EAAO,CAAA,CAAE,KAAK,CAAA;AAAA,MACvC,CAAC,CAAA;AAAA,IACH,CAAA,MAAO;AACL,MAAA,YAAA,CAAa,OAAA,CAAQ,cAAc,MAAM,CAAA;AAAA,IAC3C;AAEA,IAAA,IAAI,UAAA,EAAY,UAAU,MAAA,EAAW;AACnC,MAAA,YAAA,CAAa,KAAA,CAAM,WAAW,KAAK,CAAA;AAAA,IACrC;AAEA,IAAA,IAAI,UAAA,EAAY,WAAW,MAAA,EAAW;AACpC,MAAA,YAAA,CAAa,MAAA,CAAO,WAAW,MAAM,CAAA;AAAA,IACvC;AAEA,IAAA,MAAM,CAAC,OAAA,EAAS,CAAC,EAAE,KAAA,EAAO,CAAC,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA,CAAI;AAAA,MAC/C,aAAa,MAAA,EAAO;AAAA,MACpB;AAAA,KACD,CAAA;AAED,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,CAAA,MAAA,MAAW;AAAA,MACnC,IAAI,MAAA,CAAO,EAAA;AAAA,MACX,IAAA,EAAM,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,IAAI,CAAA;AAAA,MAC5B,QAAQ,MAAA,CAAO,MAAA;AAAA,MACf,SAAA,EAAW,OAAO,UAAA,IAAc,MAAA;AAAA,MAChC,eAAA,EAAiB,uBAAA,CAAwB,MAAA,CAAO,iBAAiB,CAAA;AAAA,MACjE,SAAA,EAAW,uBAAA,CAAwB,MAAA,CAAO,UAAU;AAAA,KACtD,CAAE,CAAA;AAEF,IAAA,OAAO,EAAE,KAAA,EAAO,UAAA,EAAY,KAAA,EAAM;AAAA,EACpC;AAAA,EAEA,MAAM,QAAQ,MAAA,EAAyC;AACrD,IAAA,MAAM,CAAC,MAAM,CAAA,GAAI,MAAM,KAAK,EAAA,CAAiB,OAAO,CAAA,CACjD,KAAA,CAAM,EAAE,EAAA,EAAI,MAAA,EAAQ,EACpB,MAAA,EAAO;AACV,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,IAAIC,oBAAA,CAAc,CAAA,iBAAA,EAAoB,MAAM,CAAA,OAAA,CAAS,CAAA;AAAA,IAC7D;AACA,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,aAAa,MAAM,CAAA;AAAA,IACjC,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,8BAAA,EAAiC,MAAM,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,CAAA;AAAA,IACtE;AAAA,EACF;AAAA,EAEQ,aAAa,MAAA,EAAsC;AACzD,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,IAAI,CAAA;AACnC,IAAA,MAAM,UAAU,MAAA,CAAO,OAAA,GAAU,KAAK,KAAA,CAAM,MAAA,CAAO,OAAO,CAAA,GAAI,MAAA;AAC9D,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AAElC,IAAA,OAAO;AAAA,MACL,IAAI,MAAA,CAAO,EAAA;AAAA,MACX,IAAA;AAAA,MACA,QAAQ,MAAA,CAAO,MAAA;AAAA,MACf,eAAA,EAAiB,uBAAA,CAAwB,MAAA,CAAO,iBAAiB,CAAA;AAAA,MACjE,SAAA,EAAW,uBAAA,CAAwB,MAAA,CAAO,UAAU,CAAA;AAAA,MACpD,SAAA,EAAW,OAAO,UAAA,IAAc,MAAA;AAAA,MAChC,OAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,WACJ,OAAA,EACoC;AACpC,IAAA,MAAM,SAASC,OAAA,EAAK;AACpB,IAAA,MAAM,IAAA,CAAK,EAAA,CAAiB,OAAO,CAAA,CAAE,MAAA,CAAO;AAAA,MAC1C,EAAA,EAAI,MAAA;AAAA,MACJ,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAA;AAAA,MACjC,SAAS,OAAA,CAAQ,OAAA,GAAU,KAAK,SAAA,CAAU,OAAA,CAAQ,OAAO,CAAA,GAAI,MAAA;AAAA,MAC7D,UAAA,EAAY,QAAQ,SAAA,IAAa,IAAA;AAAA,MACjC,MAAA,EAAQ;AAAA,KACT,CAAA;AAED,IAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ;AAAA,MACnB,KAAA,EAAO,iBAAA;AAAA,MACP,YAAA,EAAc;AAAA,QACZ,EAAA,EAAI,MAAA;AAAA,QACJ,MAAM,OAAA,CAAQ,IAAA;AAAA,QACd,WAAW,OAAA,CAAQ,SAAA;AAAA,QACnB,MAAA,EAAQ;AAAA;AACV,KACD,CAAA;AAED,IAAA,OAAO,EAAE,MAAA,EAAO;AAAA,EAClB;AAAA,EAEA,MAAM,SAAA,GAAiD;AACrD,IAAA,OAAO,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,OAAM,EAAA,KAAM;AACrC,MAAA,MAAM,CAAC,IAAI,CAAA,GAAI,MAAM,EAAA,CAAiB,OAAO,EAC1C,KAAA,CAAM;AAAA,QACL,MAAA,EAAQ;AAAA,OACT,CAAA,CACA,KAAA,CAAM,CAAC,EACP,MAAA,EAAO;AAEV,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA,OAAO,MAAA;AAAA,MACT;AAEA,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAEhC,MAAA,MAAM,WAAA,GAAc,MAAM,EAAA,CAAiB,OAAO,EAC/C,KAAA,CAAM,EAAE,EAAA,EAAI,IAAA,CAAK,EAAA,EAAI,MAAA,EAAQ,MAAA,EAAQ,EACrC,MAAA,CAAO;AAAA,QACN,MAAA,EAAQ,YAAA;AAAA,QACR,iBAAA,EAAmB,IAAA,CAAK,EAAA,CAAG,EAAA,CAAG,GAAA,EAAI;AAAA;AAAA,QAElC,SAAS,IAAA,CAAK,iBAAA,CAAkB,IAAI,CAAA,GAAI,KAAK,OAAA,GAAU;AAAA,OACxD,CAAA;AAEH,MAAA,IAAI,cAAc,CAAA,EAAG;AACnB,QAAA,OAAO,MAAA;AAAA,MACT;AAEA,MAAA,MAAM,GAAA,GAAsB;AAAA,QAC1B,IAAI,IAAA,CAAK,EAAA;AAAA,QACT,IAAA;AAAA,QACA,MAAA,EAAQ,YAAA;AAAA,QACR,iBAAiB,IAAA,CAAK,iBAAA;AAAA,QACtB,WAAW,IAAA,CAAK,UAAA;AAAA,QAChB,SAAA,EAAW,KAAK,UAAA,IAAc,MAAA;AAAA,QAC9B,KAAA,EAAO,IAAA,CAAK,QAAA,CAAS,IAAI;AAAA,OAC3B;AAEA,MAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ;AAAA,QACnB,KAAA,EAAO,iBAAA;AAAA,QACP,YAAA,EAAc;AAAA,OACf,CAAA;AAED,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,gBAAA,CAAiB,IAAI,CAAA;AAC1C,MAAA,OAAO,EAAE,GAAG,GAAA,EAAK,OAAA,EAAQ;AAAA,IAC3B,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,cAAc,MAAA,EAA+B;AACjD,IAAA,MAAM,WAAA,GAAc,MAAM,IAAA,CAAK,EAAA,CAAiB,OAAO,CAAA,CACpD,KAAA,CAAM,EAAE,EAAA,EAAI,MAAA,EAAQ,MAAA,EAAQ,YAAA,EAAc,EAC1C,MAAA,CAAO;AAAA,MACN,iBAAA,EAAmB,IAAA,CAAK,EAAA,CAAG,EAAA,CAAG,GAAA;AAAI,KACnC,CAAA;AACH,IAAA,IAAI,gBAAgB,CAAA,EAAG;AACrB,MAAA,MAAM,IAAIC,oBAAA,CAAc,CAAA,4BAAA,EAA+B,MAAM,CAAA,MAAA,CAAQ,CAAA;AAAA,IACvE;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,OAAA,EAElB;AACD,IAAA,MAAM,EAAE,UAAS,GAAI,OAAA;AACrB,IAAA,MAAM,iBAAA,GAAoBC,0BAAA,CAAoB,QAAA,EAAU,IAAA,CAAK,EAAE,CAAA;AAC/D,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,EAAA,CAAiB,OAAO,CAAA,CAChD,KAAA,CAAM,QAAA,EAAU,YAAY,CAAA,CAC5B,QAAA,CAAS,mBAAA,EAAqB,MAAM,iBAAiB,CAAA;AACxD,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,CAAA,GAAA,MAAQ;AAAA,MAChC,QAAA,EAAW,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA,CAAe,qBAAA;AAAA,MAC7C,QAAQ,GAAA,CAAI;AAAA,KACd,CAAE,CAAA;AACF,IAAA,OAAO,EAAE,KAAA,EAAM;AAAA,EACjB;AAAA,EAEA,MAAM,aAAa,OAAA,EAID;AAChB,IAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAA,EAAU,GAAI,OAAA;AAEtC,IAAA,IAAI,SAAA;AACJ,IAAA,IAAI,CAAC,QAAA,EAAU,WAAA,EAAa,WAAW,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,EAAG;AACzD,MAAA,SAAA,GAAY,YAAA;AAAA,IACd,CAAA,MAAO;AACL,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,8BAAA,EAAiC,MAAM,CAAA,aAAA,EAAgB,MAAM,CAAA,CAAA;AAAA,OAC/D;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,OAAM,EAAA,KAAM;AACpC,MAAA,MAAM,CAAC,IAAI,CAAA,GAAI,MAAM,EAAA,CAAiB,OAAO,EAC1C,KAAA,CAAM;AAAA,QACL,EAAA,EAAI;AAAA,OACL,CAAA,CACA,KAAA,CAAM,CAAC,EACP,MAAA,EAAO;AAEV,MAAA,MAAM,UAAA,GAAa,OAAO,QAAA,KAGpB;AACJ,QAAA,MAAM,WAAA,GAAc,MAAM,EAAA,CAAiB,OAAO,EAC/C,KAAA,CAAM,QAAQ,EACd,MAAA,CAAO;AAAA,UACN,MAAA;AAAA,UACA,OAAA,EAAS;AAAA,SACV,CAAA;AAEH,QAAA,IAAI,gBAAgB,CAAA,EAAG;AACrB,UAAA,MAAM,IAAID,oBAAA;AAAA,YACR,CAAA,4BAAA,EAA+B,MAAM,CAAA,aAAA,EAAgB,MAAM,CAAA;AAAA,WAC7D;AAAA,QACF;AAEA,QAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ;AAAA,UACnB,KAAA,EAAO,iBAAA;AAAA,UACP,YAAA,EAAc;AAAA,YACZ,EAAA,EAAI,MAAA;AAAA,YACJ,MAAA;AAAA,YACA,iBAAiB,IAAA,CAAK,iBAAA;AAAA,YACtB,WAAW,IAAA,CAAK,UAAA;AAAA,YAChB,WAAW,IAAA,CAAK,UAAA;AAAA,YAChB,KAAA,EAAO,IAAA,CAAK,QAAA,CAAS,IAAI;AAAA;AAC3B,SACD,CAAA;AAED,QAAA,MAAM,EAAA,CAAsB,aAAa,CAAA,CACtC,MAAA,CAAO;AAAA,UACN,OAAA,EAAS,MAAA;AAAA,UACT,UAAA,EAAY,YAAA;AAAA,UACZ,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,SAAS;AAAA,SAC/B,CAAA,CACA,SAAA,CAAU,IAAI,CAAA;AAAA,MACnB,CAAA;AAEA,MAAA,IAAI,WAAW,WAAA,EAAa;AAC1B,QAAA,MAAM,UAAA,CAAW;AAAA,UACf,EAAA,EAAI;AAAA,SACL,CAAA;AACD,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,IAAA,CAAK,WAAW,WAAA,EAAa;AAC/B,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,MAAM,CAAA,MAAA,CAAQ,CAAA;AAAA,MACvD;AACA,MAAA,IAAI,IAAA,CAAK,WAAW,SAAA,EAAW;AAC7B,QAAA,MAAM,IAAIA,oBAAA;AAAA,UACR,CAAA,kCAAA,EAAqC,MAAM,CAAA,aAAA,EAAgB,MAAM,yBACxC,IAAA,CAAK,MAAM,gBAAgB,SAAS,CAAA,CAAA;AAAA,SAC/D;AAAA,MACF;AAEA,MAAA,MAAM,UAAA,CAAW;AAAA,QACf,EAAA,EAAI,MAAA;AAAA,QACJ,MAAA,EAAQ;AAAA,OACT,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,aACJ,OAAA,EACe;AACf,IAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAK,GAAI,OAAA;AACzB,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAC1C,IAAA,MAAM,IAAA,CAAK,EAAA,CAAsB,aAAa,CAAA,CAC3C,MAAA,CAAO;AAAA,MACN,OAAA,EAAS,MAAA;AAAA,MACT,UAAA,EAAY,KAAA;AAAA,MACZ,IAAA,EAAM;AAAA,KACP,CAAA,CACA,SAAA,CAAU,IAAI,CAAA;AAAA,EACnB;AAAA,EAEA,MAAM,YAAA,CAAa,EAAE,MAAA,EAAO,EAK1B;AACA,IAAA,MAAM,CAAC,MAAM,CAAA,GAAI,MAAM,KAAK,EAAA,CAAiB,OAAO,CAAA,CACjD,KAAA,CAAM,EAAE,EAAA,EAAI,MAAA,EAAQ,CAAA,CACpB,OAAO,OAAO,CAAA;AACjB,IAAA,OAAO,OAAO,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,KAAK,CAAA,GAAI,MAAA;AAAA,EACnD;AAAA,EAEA,MAAM,cAAc,OAAA,EAGF;AAChB,IAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,MAAA,MAAM,kBAAkB,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,OAAA,CAAQ,OAAO,CAAA;AAC/D,MAAA,MAAM,IAAA,CAAK,EAAA,CAAiB,OAAO,CAAA,CAChC,KAAA,CAAM,EAAE,EAAA,EAAI,OAAA,CAAQ,MAAA,EAAQ,CAAA,CAC5B,MAAA,CAAO;AAAA,QACN,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACL;AAAA,EACF;AAAA,EAEA,MAAM,WACJ,OAAA,EAC4C;AAC5C,IAAA,MAAM,EAAE,iBAAA,EAAmB,MAAA,EAAQ,KAAA,EAAM,GAAI,OAAA;AAC7C,IAAA,MAAM,YAAY,MAAM,IAAA,CAAK,EAAA,CAAsB,aAAa,EAC7D,KAAA,CAAM;AAAA,MACL,OAAA,EAAS;AAAA,KACV,CAAA,CACA,QAAA,CAAS,CAAA,OAAA,KAAW;AACnB,MAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,QAAA,OAAA,CAAQ,MAAM,IAAA,EAAM,GAAA,EAAK,KAAK,CAAA,CAAE,OAAA,CAAQ,cAAc,YAAY,CAAA;AAAA,MACpE;AAAA,IACF,CAAC,CAAA,CACA,OAAA,CAAQ,IAAI,EACZ,MAAA,EAAO;AAEV,IAAA,MAAM,MAAA,GAAS,SAAA,CAAU,GAAA,CAAI,CAAA,KAAA,KAAS;AACpC,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA;AAClC,QAAA,OAAO;AAAA,UACL,EAAA,EAAI,MAAA,CAAO,KAAA,CAAM,EAAE,CAAA;AAAA,UACnB,iBAAA;AAAA,UACA,MAAA;AAAA,UACA,IAAA;AAAA,UACA,MAAM,KAAA,CAAM,UAAA;AAAA,UACZ,SAAA,EAAW,uBAAA,CAAwB,KAAA,CAAM,UAAU;AAAA,SACrD;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,gDAAgD,MAAM,CAAA,IAAA,EAAO,KAAA,CAAM,EAAE,KAAK,KAAK,CAAA;AAAA,SACjF;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAED,IAAA,OAAOE,8CAA2B,MAAM,CAAA;AAAA,EAC1C;AAAA,EAEA,MAAM,aAAa,OAAA,EAAsD;AACvE,IAAA,MAAM,EAAE,QAAO,GAAI,OAAA;AACnB,IAAA,MAAM,OAAA,GAAU,CAAA,wDAAA,CAAA;AAEhB,IAAA,MAAM,gBAAA,GAAA,CAAoB,MAAM,IAAA,CAAK,UAAA,CAAW,EAAE,MAAA,EAAQ,GAAG,MAAA,CAAO,MAAA;AAAA,MAClE,CAAC,EAAE,IAAA,EAAK,KAAM,IAAA,EAAM;AAAA,KACtB;AAEA,IAAA,MAAM,iBAAiB,gBAAA,CACpB,MAAA;AAAA,MACC,CAAC,EAAE,IAAA,EAAM,EAAE,QAAO,EAAE,KAAM,MAAA,KAAW,QAAA,IAAY,MAAA,KAAW;AAAA,KAC9D,CACC,GAAA,CAAI,CAAA,IAAA,KAAQ,IAAA,CAAK,KAAK,MAAM,CAAA;AAE/B,IAAA,MAAM,mBAAA,GAAsB,gBAAA,CACzB,MAAA,CAAO,CAAC,EAAE,MAAM,EAAE,MAAA,EAAO,EAAE,KAAM,MAAA,KAAW,YAAY,EACxD,GAAA,CAAI,CAAA,KAAA,KAAS,KAAA,CAAM,IAAA,CAAK,MAAM,CAAA,CAC9B,MAAA,CAAO,CAAA,IAAA,KAAQ,CAAC,cAAA,CAAe,QAAA,CAAS,IAAI,CAAC,CAAA;AAEhD,IAAA,KAAA,MAAW,QAAQ,mBAAA,EAAqB;AACtC,MAAA,MAAM,KAAK,YAAA,CAAa;AAAA,QACtB,MAAA;AAAA,QACA,IAAA,EAAM;AAAA,UACJ,OAAA;AAAA,UACA,MAAA,EAAQ,IAAA;AAAA,UACR,MAAA,EAAQ;AAAA;AACV,OACD,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,KAAK,YAAA,CAAa;AAAA,MACtB,MAAA;AAAA,MACA,MAAA,EAAQ,QAAA;AAAA,MACR,SAAA,EAAW;AAAA,QACT;AAAA;AACF,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,mBAAmB,OAAA,EAGP;AAChB,IAAA,MAAM,CAAC,MAAM,CAAA,GAAI,MAAM,IAAA,CAAK,GAAiB,OAAO,CAAA,CACjD,KAAA,CAAM,EAAE,IAAI,OAAA,CAAQ,MAAA,EAAQ,CAAA,CAC5B,OAAO,WAAW,CAAA;AAErB,IAAA,MAAMC,sBAAA,CAAiB;AAAA,MACrB,MAAM,OAAA,CAAQ,UAAA;AAAA,MACd,QAAQ,MAAA,CAAO;AAAA,KAChB,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,cAAA,CAAe,EAAE,MAAA,EAAO,EAAsC;AAClE,IAAA,MAAM,IAAA,CAAK,EAAA,CAAG,OAAO,CAAA,CAAE,KAAA,CAAM,EAAE,EAAA,EAAI,MAAA,EAAQ,CAAA,CAAE,MAAA,CAAO;AAAA,MAClD,SAAA,EAAW;AAAA,KACZ,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,mBAAmB,OAAA,EAGP;AAChB,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,MAAM,SAAA,GAAA,CAAa,MAAMC,wBAAA,CAAmB,OAAO,CAAA,EAAG,QAAA;AACtD,MAAA,MAAM,IAAA,CAAK,EAAA,CAAiB,OAAO,CAAA,CAChC,KAAA,CAAM,EAAE,EAAA,EAAI,OAAA,CAAQ,MAAA,EAAQ,CAAA,CAC5B,MAAA,CAAO;AAAA,QACN;AAAA,OACD,CAAA;AAAA,IACL;AAAA,EACF;AAAA,EAEA,MAAM,WACJ,OAAA,EACe;AACf,IAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAK,GAAI,OAAA;AACzB,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAC1C,IAAA,MAAM,CAAC,GAAG,CAAA,GAAI,MAAM,KAAK,EAAA,CAAsB,aAAa,EACzD,MAAA,CAAO;AAAA,MACN,OAAA,EAAS,MAAA;AAAA,MACT,UAAA,EAAY,WAAA;AAAA,MACZ,IAAA,EAAM;AAAA,KACP,CAAA,CACA,SAAA,CAAU,IAAI,CAAA;AAEjB,IAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ;AAAA,MACnB,KAAA,EAAO,iBAAA;AAAA,MACP,YAAA,EAAc;AAAA,QACZ,IAAI,GAAA,CAAI,EAAA;AAAA,QACR,MAAA;AAAA,QACA,MAAA,EAAQ,WAAA;AAAA,QACR;AAAA;AACF,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,OAAA,EAGE;AAChB,IAAA,MAAM,EAAE,OAAA,EAAS,MAAA,EAAO,GAAI,OAAA;AAE5B,IAAA,MAAM,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,OAAM,EAAA,KAAM;AACpC,MAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAiB,OAAO,EAC1C,KAAA,CAAM,IAAA,EAAM,MAAM,CAAA,CAClB,MAAA;AAAA,QACC;AAAA,UACE,GAAI,OAAA,IAAW,EAAE,SAAS,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA,EAAE;AAAA,UAClD,MAAA,EAAQ,MAAA;AAAA,UACR,iBAAA,EAAmB,IAAA,CAAK,EAAA,CAAG,EAAA,CAAG,GAAA;AAAI,SACpC;AAAA,QACA,CAAC,MAAM,MAAM;AAAA,OACf;AAEF,MAAA,KAAA,MAAW,EAAE,EAAA,EAAI,IAAA,EAAK,IAAK,MAAA,EAAQ;AACjC,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,IAAc,CAAA;AAQ1C,QAAA,MAAM,GAAsB,aAAa,CAAA,CACtC,MAAM,SAAA,EAAW,EAAE,EACnB,QAAA,CAAS,CAAA,CAAA,KAAK,CAAA,CAAE,OAAA,CAAQ,cAAc,CAAC,WAAA,EAAa,YAAY,CAAC,CAAC,EAClE,GAAA,EAAI;AAEP,QAAA,MAAM,EAAA,CAAsB,aAAa,CAAA,CAAE,MAAA,CAAO;AAAA,UAChD,OAAA,EAAS,EAAA;AAAA,UACT,UAAA,EAAY,WAAA;AAAA,UACZ,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,YACnB,eAAA,EACE,QAAA,CAAS,qBAAA,EAAuB,qBAAA,IAAyB;AAAA,WAC5D;AAAA,SACF,CAAA;AAAA,MACH;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,aACJ,OAAA,EAC4B;AAC5B,IAAA,MAAM,mBAA6B,EAAC;AACpC,IAAA,MAAM,WAAWC,cAAA,CAAS,UAAA,CAAW,QAAQ,OAAO,CAAA,CAAE,GAAG,SAAS,CAAA;AAElE,IAAA,MAAM,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,OAAM,EAAA,KAAM;AACpC,MAAA,MAAM,iBAAA,GAAoBJ,0BAAA,CAAoB,QAAA,EAAU,IAAA,CAAK,EAAE,CAAA;AAE/D,MAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAiB,OAAO,CAAA,CAC1C,KAAA,CAAM,QAAA,EAAU,YAAY,CAAA,CAC5B,QAAA,CAAS,mBAAA,EAAqB,IAAA,EAAM,iBAAiB,CAAA,CACrD,MAAA;AAAA,QACC;AAAA,UACE,MAAA,EAAQ,MAAA;AAAA,UACR,iBAAA,EAAmB,IAAA,CAAK,EAAA,CAAG,EAAA,CAAG,GAAA;AAAI,SACpC;AAAA,QACA,CAAC,MAAM,MAAM;AAAA,OACf;AAEF,MAAA,gBAAA,CAAiB,KAAK,GAAG,MAAA,CAAO,IAAI,CAAA,CAAA,KAAK,CAAA,CAAE,EAAE,CAAC,CAAA;AAE9C,MAAA,KAAA,MAAW,EAAE,EAAA,EAAI,IAAA,EAAK,IAAK,MAAA,EAAQ;AACjC,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,IAAc,CAAA;AAC1C,QAAA,MAAM,KAAA,GAAQ;AAAA,UACZ,eAAA,EACE,QAAA,CAAS,qBAAA,EAAuB,qBAAA,IAAyB;AAAA,SAC7D;AACA,QAAA,MAAM,CAAC,GAAG,CAAA,GAAI,MAAM,EAAA,CAAsB,aAAa,EACpD,MAAA,CAAO;AAAA,UACN,OAAA,EAAS,EAAA;AAAA,UACT,UAAA,EAAY,WAAA;AAAA,UACZ,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,KAAK;AAAA,SAC3B,CAAA,CACA,SAAA,CAAU,IAAI,CAAA;AAEjB,QAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ;AAAA,UACnB,KAAA,EAAO,iBAAA;AAAA,UACP,YAAA,EAAc;AAAA,YACZ,IAAI,GAAA,CAAI,EAAA;AAAA,YACR,MAAA,EAAQ,EAAA;AAAA,YACR,MAAA,EAAQ,WAAA;AAAA,YACR,IAAA,EAAM;AAAA;AACR,SACD,CAAA;AAAA,MACH;AAAA,IACF,CAAC,CAAA;AAED,IAAA,OAAO,EAAE,KAAK,gBAAA,EAAiB;AAAA,EACjC;AACF;;;;"}
1
+ {"version":3,"file":"DatabaseTaskStore.cjs.js","sources":["../../../src/scaffolder/tasks/DatabaseTaskStore.ts"],"sourcesContent":["/*\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\nimport { JsonObject } from '@backstage/types';\nimport {\n DatabaseService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { randomUUID as uuid } from 'node:crypto';\nimport {\n TaskStore,\n TaskStoreCreateTaskOptions,\n TaskStoreCreateTaskResult,\n TaskStoreEmitOptions,\n TaskStoreListEventsOptions,\n TaskStoreRecoverTaskOptions,\n TaskStoreShutDownTaskOptions,\n} from './types';\nimport {\n SerializedTask,\n SerializedTaskEvent,\n TaskEventType,\n TaskFilter,\n TaskSecrets,\n TaskStatus,\n} from '@backstage/plugin-scaffolder-node';\nimport { DateTime, Duration } from 'luxon';\nimport { TaskRecovery, TaskSpec } from '@backstage/plugin-scaffolder-common';\nimport { trimEventsTillLastRecovery } from './taskRecoveryHelper';\nimport { intervalFromNowTill } from './dbUtil';\nimport {\n restoreWorkspace,\n serializeWorkspace,\n} from '@backstage/plugin-scaffolder-node/alpha';\nimport { flattenParams } from '../../service/helpers';\nimport { EventsService } from '@backstage/plugin-events-node';\nimport { PermissionCriteria } from '@backstage/plugin-permission-common';\nimport {\n isAndCriteria,\n isNotCriteria,\n isOrCriteria,\n} from '@backstage/plugin-permission-node';\nimport { TaskFilters } from '@backstage/plugin-scaffolder-node';\nimport { compact } from 'lodash';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-scaffolder-backend',\n 'migrations',\n);\n\nexport type RawDbTaskRow = {\n id: string;\n spec: string;\n status: TaskStatus;\n state?: string;\n last_heartbeat_at?: string;\n created_at: string;\n created_by: string | null;\n secrets?: string | null;\n workspace?: Buffer;\n};\n\nexport type RawDbTaskEventRow = {\n id: number;\n task_id: string;\n body: string;\n event_type: TaskEventType;\n created_at: string;\n};\n\n/**\n * DatabaseTaskStore\n */\nexport type DatabaseTaskStoreOptions = {\n database: DatabaseService | Knex;\n events?: EventsService;\n};\n\n/**\n * Type guard to help DatabaseTaskStore understand when database is DatabaseService vs. when database is a Knex instance.\n * */\nfunction isDatabaseService(\n opt: DatabaseService | Knex,\n): opt is DatabaseService {\n return (opt as DatabaseService).getClient !== undefined;\n}\n\nconst parseSqlDateToIsoString = <T>(input: T): T | string => {\n if (typeof input === 'string') {\n const parsed = DateTime.fromSQL(input, { zone: 'UTC' });\n if (!parsed.isValid) {\n throw new Error(\n `Failed to parse database timestamp '${input}', ${parsed.invalidReason}: ${parsed.invalidExplanation}`,\n );\n }\n return parsed.toISO()!;\n }\n\n return input;\n};\n\n/**\n * DatabaseTaskStore\n */\nexport class DatabaseTaskStore implements TaskStore {\n private readonly db: Knex;\n private readonly events?: EventsService;\n\n static async create(\n options: DatabaseTaskStoreOptions,\n ): Promise<DatabaseTaskStore> {\n const { database } = options;\n const client = await this.getClient(database);\n\n await this.runMigrations(database, client);\n\n return new DatabaseTaskStore(client, options.events);\n }\n\n private isRecoverableTask(spec: TaskSpec): boolean {\n return ['startOver'].includes(\n spec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? 'none',\n );\n }\n\n private parseSpec({ spec, id }: { spec: string; id: string }): TaskSpec {\n try {\n return JSON.parse(spec);\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${id}', ${error}`);\n }\n }\n\n private parseTaskSecrets(taskRow: RawDbTaskRow): TaskSecrets | undefined {\n try {\n return taskRow.secrets ? JSON.parse(taskRow.secrets) : undefined;\n } catch (error) {\n throw new Error(\n `Failed to parse secrets of task '${taskRow.id}', ${error}`,\n );\n }\n }\n\n private static async getClient(\n database: DatabaseService | Knex,\n ): Promise<Knex> {\n if (isDatabaseService(database)) {\n return database.getClient();\n }\n\n return database;\n }\n\n private static async runMigrations(\n database: DatabaseService | Knex,\n client: Knex,\n ): Promise<void> {\n if (!isDatabaseService(database)) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n\n return;\n }\n\n if (!database.migrations?.skip) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n }\n\n private constructor(client: Knex, events?: EventsService) {\n this.db = client;\n this.events = events;\n }\n\n private getState(task: RawDbTaskRow) {\n try {\n return task.state ? JSON.parse(task.state).state : undefined;\n } catch (error) {\n throw new Error(\n `Failed to parse state of the task '${task.id}', ${error}`,\n );\n }\n }\n\n private isTaskFilter(filter: any): filter is TaskFilter {\n return filter.hasOwnProperty('key');\n }\n\n private parseFilter(\n filter: PermissionCriteria<TaskFilters>,\n query: Knex.QueryBuilder,\n db: Knex,\n negate: boolean = false,\n ): Knex.QueryBuilder {\n if (isNotCriteria(filter)) {\n return this.parseFilter(filter.not, query, db, !negate);\n }\n\n if (this.isTaskFilter(filter)) {\n const values: string[] = compact(filter.values) ?? [];\n if (negate) {\n query.whereNotIn(filter.key, values);\n } else {\n query.whereIn(filter.key, values);\n }\n\n return query;\n }\n\n return query[negate ? 'andWhereNot' : 'andWhere'](subQuery => {\n if (isOrCriteria(filter)) {\n for (const subFilter of filter.anyOf ?? []) {\n subQuery.orWhere(subQueryInner =>\n this.parseFilter(subFilter, subQueryInner, db, false),\n );\n }\n } else if (isAndCriteria(filter)) {\n for (const subFilter of filter.allOf ?? []) {\n subQuery.andWhere(subQueryInner =>\n this.parseFilter(subFilter, subQueryInner, db, false),\n );\n }\n }\n });\n }\n\n async list(options: {\n createdBy?: string;\n status?: TaskStatus;\n filters?: {\n createdBy?: string | string[];\n status?: TaskStatus | TaskStatus[];\n };\n pagination?: {\n limit?: number;\n offset?: number;\n };\n order?: { order: 'asc' | 'desc'; field: string }[];\n permissionFilters?: PermissionCriteria<TaskFilters>;\n }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }> {\n const { createdBy, status, pagination, order, filters, permissionFilters } =\n options ?? {};\n const queryBuilder = this.db<RawDbTaskRow & { count: number }>('tasks');\n\n const createdByValues = flattenParams<string>(\n createdBy,\n filters?.createdBy,\n );\n\n const combinedPermissionFilters:\n | PermissionCriteria<TaskFilters>\n | undefined =\n createdByValues.length > 0\n ? {\n allOf: [\n { key: 'created_by', values: createdByValues },\n ...(permissionFilters ? [permissionFilters] : []),\n ],\n }\n : permissionFilters;\n\n if (combinedPermissionFilters) {\n this.parseFilter(combinedPermissionFilters, queryBuilder, this.db);\n }\n\n if (status || filters?.status) {\n const arr: TaskStatus[] = flattenParams<TaskStatus>(\n status,\n filters?.status,\n );\n queryBuilder.whereIn('status', [...new Set(arr)]);\n }\n\n const countQuery = queryBuilder.clone();\n countQuery.count('tasks.id', { as: 'count' });\n\n if (order) {\n order.forEach(f => {\n queryBuilder.orderBy(f.field, f.order);\n });\n } else {\n queryBuilder.orderBy('created_at', 'desc');\n }\n\n if (pagination?.limit !== undefined) {\n queryBuilder.limit(pagination.limit);\n }\n\n if (pagination?.offset !== undefined) {\n queryBuilder.offset(pagination.offset);\n }\n\n const [results, [{ count }]] = await Promise.all([\n queryBuilder.select(),\n countQuery,\n ]);\n\n const tasks = results.map(result => ({\n id: result.id,\n spec: JSON.parse(result.spec),\n status: result.status,\n createdBy: result.created_by ?? undefined,\n lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),\n createdAt: parseSqlDateToIsoString(result.created_at),\n }));\n\n return { tasks, totalTasks: count };\n }\n\n async getTask(taskId: string): Promise<SerializedTask> {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId })\n .select();\n if (!result) {\n throw new NotFoundError(`No task with id '${taskId}' found`);\n }\n try {\n return this.parseTaskRow(result);\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${taskId}', ${error}`);\n }\n }\n\n private parseTaskRow(result: RawDbTaskRow): SerializedTask {\n const spec = JSON.parse(result.spec);\n const secrets = result.secrets ? JSON.parse(result.secrets) : undefined;\n const state = this.getState(result);\n\n return {\n id: result.id,\n spec,\n status: result.status,\n lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),\n createdAt: parseSqlDateToIsoString(result.created_at),\n createdBy: result.created_by ?? undefined,\n secrets,\n state,\n };\n }\n\n async createTask(\n options: TaskStoreCreateTaskOptions,\n ): Promise<TaskStoreCreateTaskResult> {\n const taskId = uuid();\n await this.db<RawDbTaskRow>('tasks').insert({\n id: taskId,\n spec: JSON.stringify(options.spec),\n secrets: options.secrets ? JSON.stringify(options.secrets) : undefined,\n created_by: options.createdBy ?? null,\n status: 'open',\n });\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: {\n id: taskId,\n spec: options.spec,\n createdBy: options.createdBy,\n status: 'open',\n },\n });\n\n return { taskId };\n }\n\n async claimTask(): Promise<SerializedTask | undefined> {\n return this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n status: 'open',\n })\n .limit(1)\n .select();\n\n if (!task) {\n return undefined;\n }\n\n const spec = this.parseSpec(task);\n\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where({ id: task.id, status: 'open' })\n .update({\n status: 'processing',\n last_heartbeat_at: this.db.fn.now(),\n // remove the secrets for non-recoverable tasks when moving to processing state.\n secrets: this.isRecoverableTask(spec) ? task.secrets : null,\n });\n\n if (updateCount < 1) {\n return undefined;\n }\n\n const ret: SerializedTask = {\n id: task.id,\n spec,\n status: 'processing',\n lastHeartbeatAt: task.last_heartbeat_at,\n createdAt: task.created_at,\n createdBy: task.created_by ?? undefined,\n state: this.getState(task),\n };\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: ret,\n });\n\n const secrets = this.parseTaskSecrets(task);\n return { ...ret, secrets };\n });\n }\n\n async heartbeatTask(taskId: string): Promise<void> {\n const updateCount = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId, status: 'processing' })\n .update({\n last_heartbeat_at: this.db.fn.now(),\n });\n if (updateCount === 0) {\n throw new ConflictError(`No running task with taskId ${taskId} found`);\n }\n }\n\n async listStaleTasks(options: { timeoutS: number }): Promise<{\n tasks: { taskId: string; recovery?: TaskRecovery }[];\n }> {\n const { timeoutS } = options;\n const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);\n const rawRows = await this.db<RawDbTaskRow>('tasks')\n .where('status', 'processing')\n .andWhere('last_heartbeat_at', '<=', heartbeatInterval);\n const tasks = rawRows.map(row => ({\n recovery: (JSON.parse(row.spec) as TaskSpec).EXPERIMENTAL_recovery,\n taskId: row.id,\n }));\n return { tasks };\n }\n\n async completeTask(options: {\n taskId: string;\n status: TaskStatus;\n eventBody: JsonObject;\n }): Promise<void> {\n const { taskId, status, eventBody } = options;\n\n let oldStatus: TaskStatus;\n if (['failed', 'completed', 'cancelled'].includes(status)) {\n oldStatus = 'processing';\n } else {\n throw new Error(\n `Invalid status update of run '${taskId}' to status '${status}'`,\n );\n }\n\n await this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n id: taskId,\n })\n .limit(1)\n .select();\n\n const updateTask = async (criteria: {\n id: string;\n status?: TaskStatus;\n }) => {\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where(criteria)\n .update({\n status,\n secrets: null,\n });\n\n if (updateCount !== 1) {\n throw new ConflictError(\n `Failed to update status to '${status}' for taskId ${taskId}`,\n );\n }\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: {\n id: taskId,\n status: status,\n lastHeartbeatAt: task.last_heartbeat_at,\n createdAt: task.created_at,\n createdBy: task.created_by,\n state: this.getState(task),\n },\n });\n\n await tx<RawDbTaskEventRow>('task_events')\n .insert({\n task_id: taskId,\n event_type: 'completion',\n body: JSON.stringify(eventBody),\n })\n .returning('id');\n };\n\n if (status === 'cancelled') {\n await updateTask({\n id: taskId,\n });\n return;\n }\n\n if (task.status === 'cancelled') {\n return;\n }\n\n if (!task) {\n throw new Error(`No task with taskId ${taskId} found`);\n }\n if (task.status !== oldStatus) {\n throw new ConflictError(\n `Refusing to update status of run '${taskId}' to status '${status}' ` +\n `as it is currently '${task.status}', expected '${oldStatus}'`,\n );\n }\n\n await updateTask({\n id: taskId,\n status: oldStatus,\n });\n });\n }\n\n async emitLogEvent(\n options: TaskStoreEmitOptions<{ message: string } & JsonObject>,\n ): Promise<void> {\n const { taskId, body } = options;\n const serializedBody = JSON.stringify(body);\n await this.db<RawDbTaskEventRow>('task_events')\n .insert({\n task_id: taskId,\n event_type: 'log',\n body: serializedBody,\n })\n .returning('id');\n }\n\n async getTaskState({ taskId }: { taskId: string }): Promise<\n | {\n state: JsonObject;\n }\n | undefined\n > {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId })\n .select('state');\n return result.state ? JSON.parse(result.state) : undefined;\n }\n\n async saveTaskState(options: {\n taskId: string;\n state?: JsonObject;\n }): Promise<void> {\n if (options.state) {\n const serializedState = JSON.stringify({ state: options.state });\n await this.db<RawDbTaskRow>('tasks')\n .where({ id: options.taskId })\n .update({\n state: serializedState,\n });\n }\n }\n\n async listEvents(\n options: TaskStoreListEventsOptions,\n ): Promise<{ events: SerializedTaskEvent[] }> {\n const { isTaskRecoverable, taskId, after } = options;\n const rawEvents = await this.db<RawDbTaskEventRow>('task_events')\n .where({\n task_id: taskId,\n })\n .andWhere(builder => {\n if (typeof after === 'number') {\n builder.where('id', '>', after).orWhere('event_type', 'completion');\n }\n })\n .orderBy('id')\n .select();\n\n const events = rawEvents.map(event => {\n try {\n const body = JSON.parse(event.body) as SerializedTaskEvent['body'];\n return {\n id: Number(event.id),\n isTaskRecoverable,\n taskId,\n body,\n type: event.event_type,\n createdAt: parseSqlDateToIsoString(event.created_at),\n };\n } catch (error) {\n throw new Error(\n `Failed to parse event body from event taskId=${taskId} id=${event.id}, ${error}`,\n );\n }\n });\n\n return trimEventsTillLastRecovery(events);\n }\n\n async shutdownTask(options: TaskStoreShutDownTaskOptions): Promise<void> {\n const { taskId } = options;\n const message = `This task was marked as stale as it exceeded its timeout`;\n\n const statusStepEvents = (await this.listEvents({ taskId })).events.filter(\n ({ body }) => body?.stepId,\n );\n\n const completedSteps = statusStepEvents\n .filter(\n ({ body: { status } }) => status === 'failed' || status === 'completed',\n )\n .map(step => step.body.stepId);\n\n const hungProcessingSteps = statusStepEvents\n .filter(({ body: { status } }) => status === 'processing')\n .map(event => event.body.stepId)\n .filter(step => !completedSteps.includes(step));\n\n for (const step of hungProcessingSteps) {\n await this.emitLogEvent({\n taskId,\n body: {\n message,\n stepId: step,\n status: 'failed',\n },\n });\n }\n\n await this.completeTask({\n taskId,\n status: 'failed',\n eventBody: {\n message,\n },\n });\n }\n\n async rehydrateWorkspace(options: {\n taskId: string;\n targetPath: string;\n }): Promise<void> {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: options.taskId })\n .select('workspace');\n\n await restoreWorkspace({\n path: options.targetPath,\n buffer: result.workspace,\n });\n }\n\n async cleanWorkspace({ taskId }: { taskId: string }): Promise<void> {\n await this.db('tasks').where({ id: taskId }).update({\n workspace: null,\n });\n }\n\n async serializeWorkspace(options: {\n path: string;\n taskId: string;\n }): Promise<void> {\n if (options.path) {\n const workspace = (await serializeWorkspace(options)).contents;\n await this.db<RawDbTaskRow>('tasks')\n .where({ id: options.taskId })\n .update({\n workspace,\n });\n }\n }\n\n async cancelTask(\n options: TaskStoreEmitOptions<{ message: string } & JsonObject>,\n ): Promise<void> {\n const { taskId, body } = options;\n const serializedBody = JSON.stringify(body);\n const [ret] = await this.db<RawDbTaskEventRow>('task_events')\n .insert({\n task_id: taskId,\n event_type: 'cancelled',\n body: serializedBody,\n })\n .returning('id');\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: {\n id: ret.id,\n taskId,\n status: 'cancelled',\n body,\n },\n });\n }\n\n async retryTask(options: {\n secrets?: TaskSecrets;\n taskId: string;\n }): Promise<void> {\n const { secrets, taskId } = options;\n\n await this.db.transaction(async tx => {\n const result = await tx<RawDbTaskRow>('tasks')\n .where('id', taskId)\n .update(\n {\n ...(secrets && { secrets: JSON.stringify(secrets) }),\n status: 'open',\n last_heartbeat_at: this.db.fn.now(),\n },\n ['id', 'spec'],\n );\n\n for (const { id, spec } of result) {\n const taskSpec = JSON.parse(spec as string) as TaskSpec;\n\n /**\n * Once task is picked up, all event types are replayed.\n * We have to remove cancelled or completion event_type as these are as actions for frontend to perform.\n * In contrary, we send 'recovered' event_type to reset the state on the frontend side.\n *\n */\n await tx<RawDbTaskEventRow>('task_events')\n .where('task_id', id)\n .andWhere(q => q.whereIn('event_type', ['cancelled', 'completion']))\n .del();\n\n await tx<RawDbTaskEventRow>('task_events').insert({\n task_id: id,\n event_type: 'recovered',\n body: JSON.stringify({\n recoverStrategy:\n taskSpec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? 'none',\n }),\n });\n }\n });\n }\n\n async recoverTasks(\n options: TaskStoreRecoverTaskOptions,\n ): Promise<{ ids: string[] }> {\n const taskIdsToRecover: string[] = [];\n const timeoutS = Duration.fromObject(options.timeout).as('seconds');\n\n await this.db.transaction(async tx => {\n const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);\n\n const result = await tx<RawDbTaskRow>('tasks')\n .where('status', 'processing')\n .andWhere('last_heartbeat_at', '<=', heartbeatInterval)\n .update(\n {\n status: 'open',\n last_heartbeat_at: this.db.fn.now(),\n },\n ['id', 'spec'],\n );\n\n taskIdsToRecover.push(...result.map(i => i.id));\n\n for (const { id, spec } of result) {\n const taskSpec = JSON.parse(spec as string) as TaskSpec;\n const event = {\n recoverStrategy:\n taskSpec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? 'none',\n };\n const [ret] = await tx<RawDbTaskEventRow>('task_events')\n .insert({\n task_id: id,\n event_type: 'recovered',\n body: JSON.stringify(event),\n })\n .returning('id');\n\n this.events?.publish({\n topic: 'scaffolder.task',\n eventPayload: {\n id: ret.id,\n taskId: id,\n status: 'recovered',\n body: event,\n },\n });\n }\n });\n\n return { ids: taskIdsToRecover };\n }\n}\n"],"names":["resolvePackagePath","DateTime","isNotCriteria","compact","isOrCriteria","isAndCriteria","flattenParams","NotFoundError","uuid","ConflictError","intervalFromNowTill","trimEventsTillLastRecovery","restoreWorkspace","serializeWorkspace","Duration"],"mappings":"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"}
@@ -3,20 +3,20 @@
3
3
  var errors = require('@backstage/errors');
4
4
  var fs = require('fs-extra');
5
5
  var jsonschema = require('jsonschema');
6
- var nunjucks = require('nunjucks');
7
6
  var path = require('node:path');
7
+ var nunjucks = require('nunjucks');
8
8
  var winston = require('winston');
9
9
  var SecureTemplater = require('../../lib/templating/SecureTemplater.cjs.js');
10
10
  var helper = require('./helper.cjs.js');
11
11
  var pluginPermissionCommon = require('@backstage/plugin-permission-common');
12
12
  var pluginPermissionNode = require('@backstage/plugin-permission-node');
13
13
  var alpha = require('@backstage/plugin-scaffolder-common/alpha');
14
+ var defaultEnvironment = require('../../lib/defaultEnvironment.cjs.js');
14
15
  var createDefaultFilters = require('../../lib/templating/filters/createDefaultFilters.cjs.js');
15
16
  var rules = require('../../service/rules.cjs.js');
16
17
  var metrics = require('../../util/metrics.cjs.js');
17
- var logger = require('./logger.cjs.js');
18
18
  var templating = require('../../util/templating.cjs.js');
19
- var defaultEnvironment = require('../../lib/defaultEnvironment.cjs.js');
19
+ var logger = require('./logger.cjs.js');
20
20
 
21
21
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
22
22
 
@@ -39,8 +39,8 @@ function _interopNamespaceCompat(e) {
39
39
  }
40
40
 
41
41
  var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
42
- var nunjucks__default = /*#__PURE__*/_interopDefaultCompat(nunjucks);
43
42
  var path__default = /*#__PURE__*/_interopDefaultCompat(path);
43
+ var nunjucks__default = /*#__PURE__*/_interopDefaultCompat(nunjucks);
44
44
  var winston__namespace = /*#__PURE__*/_interopNamespaceCompat(winston);
45
45
 
46
46
  const isValidTaskSpec = (taskSpec) => {
@@ -434,12 +434,24 @@ class NunjucksWorkflowRunner {
434
434
  const workspacePath = path__default.default.join(this.options.workingDirectory, taskId);
435
435
  const { additionalTemplateFilters, additionalTemplateGlobals } = this.options;
436
436
  this.environment = await this.getEnvironmentConfig();
437
- const renderTemplate = await SecureTemplater.SecureTemplater.loadRenderer({
437
+ const taskState = { failed: false };
438
+ const statusCheckInvoked = { value: false };
439
+ const { render: renderTemplate, dispose } = await SecureTemplater.SecureTemplater.loadRenderer({
438
440
  templateFilters: {
439
441
  ...this.defaultTemplateFilters,
440
442
  ...additionalTemplateFilters
441
443
  },
442
- templateGlobals: additionalTemplateGlobals
444
+ templateGlobals: {
445
+ ...additionalTemplateGlobals,
446
+ always: () => {
447
+ statusCheckInvoked.value = true;
448
+ return true;
449
+ },
450
+ failure: () => {
451
+ statusCheckInvoked.value = true;
452
+ return taskState.failed;
453
+ }
454
+ }
443
455
  });
444
456
  try {
445
457
  await task.rehydrateWorkspace?.({ taskId, targetPath: workspacePath });
@@ -463,16 +475,64 @@ class NunjucksWorkflowRunner {
463
475
  [{ permission: alpha.actionExecutePermission }],
464
476
  { credentials: await task.getInitiatorCredentials() }
465
477
  ) : [{ result: pluginPermissionCommon.AuthorizeResult.ALLOW }];
478
+ let firstError;
479
+ const allErrors = [];
466
480
  for (const step of task.spec.steps) {
467
- await this.executeStep(
468
- task,
469
- step,
470
- context,
471
- renderTemplate,
472
- taskTrack,
473
- workspacePath,
474
- decision
475
- );
481
+ if (taskState.failed) {
482
+ if (typeof step.if !== "string") {
483
+ await task.emitLog(
484
+ `Skipping step ${step.id} because a previous step failed`,
485
+ { stepId: step.id, status: "skipped" }
486
+ );
487
+ continue;
488
+ }
489
+ statusCheckInvoked.value = false;
490
+ this.render(step.if, context, renderTemplate);
491
+ if (!statusCheckInvoked.value) {
492
+ await task.emitLog(
493
+ `Skipping step ${step.id} because a previous step failed`,
494
+ { stepId: step.id, status: "skipped" }
495
+ );
496
+ continue;
497
+ }
498
+ }
499
+ try {
500
+ await this.executeStep(
501
+ task,
502
+ step,
503
+ context,
504
+ renderTemplate,
505
+ taskTrack,
506
+ workspacePath,
507
+ decision
508
+ );
509
+ } catch (err) {
510
+ const error = err;
511
+ allErrors.push({ step, error });
512
+ if (!firstError) {
513
+ firstError = error;
514
+ } else {
515
+ this.options.logger.error(
516
+ `Additional error in step ${step.id} (${step.name}): ${error.message}`,
517
+ error
518
+ );
519
+ await task.emitLog(
520
+ `Additional error occurred: ${error.message}
521
+ ${error.stack}`,
522
+ { stepId: step.id, status: "failed" }
523
+ );
524
+ }
525
+ taskState.failed = true;
526
+ }
527
+ }
528
+ if (firstError) {
529
+ if (allErrors.length > 1) {
530
+ const additionalErrorSummary = allErrors.slice(1).map(({ step }) => `${step.id} (${step.name})`).join(", ");
531
+ this.options.logger.warn(
532
+ `Task failed with ${allErrors.length} errors. First error from step ${allErrors[0].step.id}. Additional failures in: ${additionalErrorSummary}`
533
+ );
534
+ }
535
+ throw firstError;
476
536
  }
477
537
  const output = this.render(task.spec.output, context, renderTemplate);
478
538
  if (Array.isArray(output?.links)) {
@@ -485,6 +545,10 @@ class NunjucksWorkflowRunner {
485
545
  await task.cleanWorkspace?.();
486
546
  return { output };
487
547
  } finally {
548
+ try {
549
+ dispose();
550
+ } catch {
551
+ }
488
552
  if (workspacePath) {
489
553
  await fs__default.default.remove(workspacePath);
490
554
  }
@@ -502,7 +566,7 @@ function scaffoldingTracker(metrics$1) {
502
566
  help: "Duration of a task run",
503
567
  labelNames: ["template", "result"]
504
568
  });
505
- const promtStepCount = metrics.createCounterMetric({
569
+ const promStepCount = metrics.createCounterMetric({
506
570
  name: "scaffolder_step_count",
507
571
  help: "Count of step runs",
508
572
  labelNames: ["template", "step", "result"]
@@ -618,7 +682,7 @@ function scaffoldingTracker(metrics$1) {
618
682
  stepId: step.id,
619
683
  status: "completed"
620
684
  });
621
- promtStepCount.inc({
685
+ promStepCount.inc({
622
686
  template,
623
687
  step: step.name,
624
688
  result: "ok"
@@ -632,7 +696,7 @@ function scaffoldingTracker(metrics$1) {
632
696
  });
633
697
  }
634
698
  async function markCancelled() {
635
- promtStepCount.inc({
699
+ promStepCount.inc({
636
700
  template,
637
701
  step: step.name,
638
702
  result: "cancelled"
@@ -646,7 +710,7 @@ function scaffoldingTracker(metrics$1) {
646
710
  });
647
711
  }
648
712
  async function markFailed() {
649
- promtStepCount.inc({
713
+ promStepCount.inc({
650
714
  template,
651
715
  step: step.name,
652
716
  result: "failed"