diff --git a/.github/workflows/rbac-perf-test.yml b/.github/workflows/rbac-perf-test.yml index 411af40748d..75874d1ff40 100644 --- a/.github/workflows/rbac-perf-test.yml +++ b/.github/workflows/rbac-perf-test.yml @@ -29,6 +29,7 @@ env: TREE_HEIGHT: ${{ github.event.inputs.tree_height || '9' }} COMPOSE_DISABLE_LINUX: '1' CI: 'true' + YARN_ENABLE_HARDENED_MODE: '0' jobs: perf-test: @@ -95,8 +96,8 @@ jobs: if: github.event_name == 'pull_request' working-directory: community-plugins run: | - git checkout ${{ steps.base-ref.outputs.ref }} -- workspaces/rbac/ - echo "Checked out base branch RBAC plugin" + git checkout ${{ steps.base-ref.outputs.ref }} -- workspaces/rbac/ yarn.lock + echo "Checked out base branch RBAC plugin + lockfile" - name: Build baseline Backstage image working-directory: backstage-rbac-performance @@ -141,8 +142,8 @@ jobs: if: github.event_name == 'pull_request' working-directory: community-plugins run: | - git checkout ${{ github.sha }} -- workspaces/rbac/ - echo "Restored PR branch RBAC plugin" + git checkout ${{ github.sha }} -- workspaces/rbac/ yarn.lock + echo "Restored PR branch RBAC plugin + lockfile" - name: Rebuild patch Backstage image if: github.event_name == 'pull_request' diff --git a/workspaces/rbac/package.json b/workspaces/rbac/package.json index 98a21491c7d..26056a270e1 100644 --- a/workspaces/rbac/package.json +++ b/workspaces/rbac/package.json @@ -69,6 +69,9 @@ "dependenciesMeta": { "better-sqlite3": { "built": true + }, + "isolated-vm": { + "built": true } }, "prettier": "@backstage/cli/config/prettier", diff --git a/workspaces/rbac/plugins/rbac-backend/__fixtures__/mock-utils.ts b/workspaces/rbac/plugins/rbac-backend/__fixtures__/mock-utils.ts index 1cc6babaf2d..e59fe91da41 100644 --- a/workspaces/rbac/plugins/rbac-backend/__fixtures__/mock-utils.ts +++ b/workspaces/rbac/plugins/rbac-backend/__fixtures__/mock-utils.ts @@ -24,13 +24,12 @@ import { AuthorizeResult } from '@backstage/plugin-permission-common'; import type { Enforcer } from 'casbin'; import * as Knex from 'knex'; -import { MockClient } from 'knex-mock-client'; +import { createTracker, MockClient } from 'knex-mock-client'; import { resolve } from 'path'; -import type TypeORMAdapter from 'typeorm-adapter'; import type { RBACProvider } from '@backstage-community/plugin-rbac-node'; -import { CasbinDBAdapterFactory } from '../src/database/casbin-adapter-factory'; +import type { CasbinKnexAdapter } from '../src/database/casbin-knex-adapter'; import { ConditionalStorage } from '../src/database/conditional-storage'; import { RoleMetadataStorage } from '../src/database/role-metadata'; import { @@ -124,9 +123,9 @@ export const enforcerDelegateMock: Partial = { updateGroupingPolicies: jest.fn().mockImplementation(), }; -export const dataBaseAdapterFactoryMock: Partial = { - createAdapter: jest.fn((): Promise => { - return Promise.resolve({} as TypeORMAdapter); +export const casbinKnexAdapterMock = { + newAdapter: jest.fn((): Promise => { + return Promise.resolve({} as CasbinKnexAdapter); }), }; @@ -138,6 +137,33 @@ export const providerMock: RBACProvider = { export const mockClientKnex = Knex.knex({ client: MockClient }); +export function setupMockCasbinRuleTracker(): ReturnType { + const tracker = createTracker(mockClientKnex); + tracker.on.select('casbin_rule').response([]); + tracker.on.insert('casbin_rule').response([]); + tracker.on.delete('casbin_rule').response(0); + return tracker; +} + +export async function createTestCasbinKnex(): Promise { + const testKnex = Knex.knex({ + client: 'better-sqlite3', + connection: { filename: ':memory:' }, + useNullAsDefault: true, + }); + await testKnex.schema.createTable('casbin_rule', table => { + table.increments('id').primary(); + table.string('ptype').nullable(); + table.string('v0').nullable(); + table.string('v1').nullable(); + table.string('v2').nullable(); + table.string('v3').nullable(); + table.string('v4').nullable(); + table.string('v5').nullable(); + }); + return testKnex; +} + export const mockHttpAuth = mockServices.httpAuth(); export const mockAuthService = mockServices.auth(); export const mockUserInfoService = mockServices.userInfo.mock(); diff --git a/workspaces/rbac/plugins/rbac-backend/__fixtures__/test-utils.ts b/workspaces/rbac/plugins/rbac-backend/__fixtures__/test-utils.ts index 4be4b3a9c53..0266bd56277 100644 --- a/workspaces/rbac/plugins/rbac-backend/__fixtures__/test-utils.ts +++ b/workspaces/rbac/plugins/rbac-backend/__fixtures__/test-utils.ts @@ -27,7 +27,7 @@ import { import * as Knex from 'knex'; import { MockClient } from 'knex-mock-client'; -import { CasbinDBAdapterFactory } from '../src/database/casbin-adapter-factory'; +import { CasbinKnexAdapter } from '../src/database/casbin-knex-adapter'; import { RoleMetadataStorage } from '../src/database/role-metadata'; import { RBACPermissionPolicy } from '../src/policies/permission-policy'; import { BackstageRoleManager } from '../src/role-manager/role-manager'; @@ -38,6 +38,7 @@ import { PluginPermissionMetadataCollector } from '../src/service/plugin-endpoin import { mockAuditorService, conditionalStorageMock, + createTestCasbinKnex, csvPermFile, mockAuthService, mockUserInfoService, @@ -91,11 +92,9 @@ export function newConfig( }); } -export async function newAdapter(config: Config): Promise { - return await new CasbinDBAdapterFactory( - config, - mockClientKnex, - ).createAdapter(); +export async function newAdapter(_config: Config): Promise { + const testKnex = await createTestCasbinKnex(); + return await CasbinKnexAdapter.newAdapter(testKnex); } export async function createEnforcer( diff --git a/workspaces/rbac/plugins/rbac-backend/migrations/20250614000000_migrations.js b/workspaces/rbac/plugins/rbac-backend/migrations/20250614000000_migrations.js new file mode 100644 index 00000000000..0fafa87f462 --- /dev/null +++ b/workspaces/rbac/plugins/rbac-backend/migrations/20250614000000_migrations.js @@ -0,0 +1,47 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Creates the casbin_rule table if it does not already exist. + * For existing installations, the TypeORM adapter created this table. + * For new installations, this migration creates it. + * + * @param {import('knex').Knex} knex + * @returns {Promise} + */ +exports.up = async function up(knex) { + const exists = await knex.schema.hasTable('casbin_rule'); + if (!exists) { + await knex.schema.createTable('casbin_rule', table => { + table.increments('id').primary(); + table.string('ptype').nullable(); + table.string('v0').nullable(); + table.string('v1').nullable(); + table.string('v2').nullable(); + table.string('v3').nullable(); + table.string('v4').nullable(); + table.string('v5').nullable(); + }); + } +}; + +/** + * @param {import('knex').Knex} _knex + * @returns {Promise} + */ +exports.down = async function down(_knex) { + // Intentionally empty — casbin_rule contains live policy data. +}; diff --git a/workspaces/rbac/plugins/rbac-backend/package.json b/workspaces/rbac/plugins/rbac-backend/package.json index ea4e7e0fb71..b11e33a14ba 100644 --- a/workspaces/rbac/plugins/rbac-backend/package.json +++ b/workspaces/rbac/plugins/rbac-backend/package.json @@ -55,7 +55,6 @@ "js-yaml": "^4.1.0", "knex": "^3.0.0", "lodash": "^4.17.21", - "typeorm-adapter": "^1.6.1", "zod": "^4.3.6" }, "devDependencies": { diff --git a/workspaces/rbac/plugins/rbac-backend/src/database/casbin-adapter-factory.test.ts b/workspaces/rbac/plugins/rbac-backend/src/database/casbin-adapter-factory.test.ts deleted file mode 100644 index 5325c828e53..00000000000 --- a/workspaces/rbac/plugins/rbac-backend/src/database/casbin-adapter-factory.test.ts +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { mockServices } from '@backstage/backend-test-utils'; - -import knex, { Knex } from 'knex'; -import TypeORMAdapter from 'typeorm-adapter'; - -import { CasbinDBAdapterFactory } from './casbin-adapter-factory'; - -jest.mock('typeorm-adapter', () => { - return { - newAdapter: jest.fn((): Promise => { - return Promise.resolve({} as TypeORMAdapter); - }), - }; -}); - -describe('CasbinAdapterFactory', () => { - let newAdapterMock: jest.Mock>; - let db: Knex; - - beforeEach(() => { - newAdapterMock = TypeORMAdapter.newAdapter as jest.Mock< - Promise - >; - jest.clearAllMocks(); - }); - - it('test building an adapter using a better-sqlite3 configuration.', async () => { - db = knex.knex({ - client: 'better-sqlite3', - connection: ':memory', - }); - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'better-sqlite3', - connection: ':memory:', - }, - }, - }, - }); - const adapterFactory = new CasbinDBAdapterFactory(config, db); - const adapter = adapterFactory.createAdapter(); - expect(adapter).not.toBeNull(); - expect(newAdapterMock).toHaveBeenCalled(); - }); - - describe('build adapter with postgres configuration', () => { - beforeEach(() => { - db = knex.knex({ - client: 'pg', - connection: { - database: 'test-database', - }, - }); - process.env.TEST = 'test'; - }); - - it('test building an adapter using a PostgreSQL configuration.', async () => { - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - port: '5432', - schema: 'public', - user: 'postgresUser', - password: process.env.TEST, - }, - }, - }, - }, - }); - const factory = new CasbinDBAdapterFactory(config, db); - const adapter = await factory.createAdapter(); - expect(adapter).not.toBeNull(); - expect(newAdapterMock).toHaveBeenCalledWith({ - type: 'postgres', - host: 'localhost', - port: 5432, - schema: 'public', - username: 'postgresUser', - password: process.env.TEST, - database: 'test-database', - ssl: undefined, - }); - }); - - it('test building an adapter using a PostgreSQL configuration with enabled ssl.', async () => { - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - port: '5432', - schema: 'public', - user: 'postgresUser', - password: process.env.TEST, - ssl: true, - }, - }, - }, - }, - }); - const factory = new CasbinDBAdapterFactory(config, db); - const adapter = await factory.createAdapter(); - expect(adapter).not.toBeNull(); - expect(newAdapterMock).toHaveBeenCalledWith({ - type: 'postgres', - host: 'localhost', - port: 5432, - schema: 'public', - username: 'postgresUser', - password: process.env.TEST, - database: 'test-database', - ssl: true, - }); - }); - - it('test building an adapter using a PostgreSQL configuration without explicit credentials.', async () => { - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - port: '5432', - schema: 'public', - }, - }, - }, - }, - }); - const factory = new CasbinDBAdapterFactory(config, db); - const adapter = await factory.createAdapter(); - expect(adapter).not.toBeNull(); - expect(newAdapterMock).toHaveBeenCalledWith({ - type: 'postgres', - host: 'localhost', - port: 5432, - schema: 'public', - username: undefined, - password: undefined, - database: 'test-database', - ssl: undefined, - }); - }); - - it('test building an adapter using a PostgreSQL configuration with intentionally disabled ssl.', async () => { - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - port: '5432', - schema: 'public', - user: 'postgresUser', - password: process.env.TEST, - ssl: false, - }, - }, - }, - }, - }); - const factory = new CasbinDBAdapterFactory(config, db); - const adapter = await factory.createAdapter(); - expect(adapter).not.toBeNull(); - expect(newAdapterMock).toHaveBeenCalledWith({ - type: 'postgres', - host: 'localhost', - port: 5432, - schema: 'public', - username: 'postgresUser', - password: process.env.TEST, - database: 'test-database', - ssl: false, - }); - }); - - it('test building an adapter using a PostgreSQL configuration with intentionally ssl and ca cert.', async () => { - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - port: '5432', - schema: 'public', - user: 'postgresUser', - password: process.env.TEST, - ssl: { - ca: 'abc', - }, - }, - }, - }, - }, - }); - const factory = new CasbinDBAdapterFactory(config, db); - const adapter = await factory.createAdapter(); - expect(adapter).not.toBeNull(); - expect(newAdapterMock).toHaveBeenCalledWith({ - type: 'postgres', - host: 'localhost', - port: 5432, - schema: 'public', - username: 'postgresUser', - password: process.env.TEST, - database: 'test-database', - ssl: { - ca: 'abc', - }, - }); - }); - - it('test building an adapter using a PostgreSQL configuration with intentionally ssl and TLS options.', async () => { - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - port: '5432', - user: 'postgresUser', - password: process.env.TEST, - ssl: { - ca: 'abc', - rejectUnauthorized: false, - }, - }, - }, - }, - }, - }); - const factory = new CasbinDBAdapterFactory(config, db); - const adapter = await factory.createAdapter(); - expect(adapter).not.toBeNull(); - expect(newAdapterMock).toHaveBeenCalledWith({ - type: 'postgres', - host: 'localhost', - port: 5432, - schema: 'public', - username: 'postgresUser', - password: process.env.TEST, - database: 'test-database', - ssl: { - ca: 'abc', - rejectUnauthorized: false, - }, - }); - }); - - it('test building an adapter using a PostgreSQL configuration with intentionally ssl without CA.', async () => { - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - host: 'localhost', - port: '5432', - user: 'postgresUser', - password: process.env.TEST, - ssl: { - rejectUnauthorized: false, - }, - }, - }, - }, - }, - }); - const factory = new CasbinDBAdapterFactory(config, db); - const adapter = await factory.createAdapter(); - expect(adapter).not.toBeNull(); - expect(newAdapterMock).toHaveBeenCalledWith({ - type: 'postgres', - host: 'localhost', - port: 5432, - schema: 'public', - username: 'postgresUser', - password: process.env.TEST, - database: 'test-database', - ssl: { - rejectUnauthorized: false, - }, - }); - }); - }); - - describe('build adapter with dynamic Knex connection resolvers', () => { - it('should resolve database name when knex connection is a dynamic resolver function', async () => { - const dynamicConnectionDb = knex.knex({ - client: 'pg', - connection: async () => ({ - host: 'myserver.postgres.database.azure.com', - port: 5432, - user: 'myuser@myserver', - password: 'mock-azure-token-1234567890', - database: 'backstage_plugin_permission', - ssl: { rejectUnauthorized: false }, - }), - }); - - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - type: 'azure', - host: 'myserver.postgres.database.azure.com', - port: '5432', - user: 'myuser@myserver', - ssl: { - rejectUnauthorized: false, - }, - }, - }, - }, - }, - }); - - const factory = new CasbinDBAdapterFactory(config, dynamicConnectionDb); - await factory.createAdapter(); - - expect(newAdapterMock).toHaveBeenCalled(); - const adapterConfig = newAdapterMock.mock.calls[0][0]; - expect(adapterConfig.database).toBe('backstage_plugin_permission'); - expect(adapterConfig.host).toBe('myserver.postgres.database.azure.com'); - expect(adapterConfig.username).toBe('myuser@myserver'); - expect(adapterConfig.ssl).toEqual({ rejectUnauthorized: false }); - expect(typeof adapterConfig.password).toBe('function'); - - await dynamicConnectionDb.destroy(); - }); - - it('should call the dynamic resolver on each password function invocation', async () => { - const connectionResolver = jest.fn(async () => ({ - host: 'myserver.postgres.database.azure.com', - port: 5432, - user: 'myuser@myserver', - password: 'mock-azure-token-1234567890', - database: 'test-database', - ssl: { rejectUnauthorized: false }, - })); - - const dynamicConnectionDb = knex.knex({ - client: 'pg', - connection: connectionResolver, - }); - - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - type: 'azure', - host: 'myserver.postgres.database.azure.com', - port: '5432', - user: 'myuser@myserver', - }, - }, - }, - }, - }); - - const factory = new CasbinDBAdapterFactory(config, dynamicConnectionDb); - await factory.createAdapter(); - - connectionResolver.mockClear(); - - const adapterConfig = newAdapterMock.mock.calls[0][0]; - const passwordFn = adapterConfig.password; - const tokenResult = await passwordFn(); - - expect(connectionResolver).toHaveBeenCalledTimes(1); - expect(tokenResult).toBe('mock-azure-token-1234567890'); - - connectionResolver.mockResolvedValueOnce({ - host: 'myserver.postgres.database.azure.com', - port: 5432, - user: 'myuser@myserver', - password: 'new-token-different', - database: 'test-database', - ssl: { rejectUnauthorized: false }, - }); - - const tokenResult2 = await passwordFn(); - expect(tokenResult2).toBe('new-token-different'); - expect(connectionResolver).toHaveBeenCalledTimes(2); - - await dynamicConnectionDb.destroy(); - }); - - it('should support RDS-style dynamic connection resolvers', async () => { - const dynamicConnectionDb = knex.knex({ - client: 'pg', - connection: async () => ({ - host: 'mydb.abc123.us-east-1.rds.amazonaws.com', - port: 5432, - user: 'backstage', - password: 'mock-rds-iam-token', - database: 'backstage_plugin_permission', - ssl: { rejectUnauthorized: true }, - }), - }); - - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - type: 'rds', - host: 'mydb.abc123.us-east-1.rds.amazonaws.com', - port: '5432', - user: 'backstage', - region: 'us-east-1', - }, - }, - }, - }, - }); - - const factory = new CasbinDBAdapterFactory(config, dynamicConnectionDb); - await factory.createAdapter(); - - expect(newAdapterMock).toHaveBeenCalled(); - const adapterConfig = newAdapterMock.mock.calls[0][0]; - expect(adapterConfig.database).toBe('backstage_plugin_permission'); - expect(adapterConfig.host).toBe( - 'mydb.abc123.us-east-1.rds.amazonaws.com', - ); - expect(typeof adapterConfig.password).toBe('function'); - - const tokenResult = await adapterConfig.password(); - expect(tokenResult).toBe('mock-rds-iam-token'); - - await dynamicConnectionDb.destroy(); - }); - - it('should throw error when dynamic resolver returns no password', async () => { - const dynamicConnectionDb = knex.knex({ - client: 'pg', - connection: async () => ({ - host: 'myserver.postgres.database.azure.com', - port: 5432, - user: 'myuser@myserver', - database: 'test-database', - }), - }); - - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - type: 'azure', - host: 'myserver.postgres.database.azure.com', - port: '5432', - user: 'myuser@myserver', - }, - }, - }, - }, - }); - - const factory = new CasbinDBAdapterFactory(config, dynamicConnectionDb); - await factory.createAdapter(); - - const adapterConfig = newAdapterMock.mock.calls[0][0]; - await expect(adapterConfig.password()).rejects.toThrow( - 'missing password on resolved Knex connection', - ); - - await dynamicConnectionDb.destroy(); - }); - - it('should throw error when dynamic resolver returns no database name', async () => { - const dynamicConnectionDb = knex.knex({ - client: 'pg', - connection: async () => ({ - host: 'myserver.postgres.database.azure.com', - port: 5432, - user: 'myuser@myserver', - password: 'mock-azure-token', - }), - }); - - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client: 'pg', - connection: { - type: 'azure', - host: 'myserver.postgres.database.azure.com', - port: '5432', - user: 'myuser@myserver', - }, - }, - }, - }, - }); - - const factory = new CasbinDBAdapterFactory(config, dynamicConnectionDb); - await expect(factory.createAdapter()).rejects.toThrow( - 'missing database name on Knex connection', - ); - - await dynamicConnectionDb.destroy(); - }); - }); - - it('ensure that building an adapter with an unknown configuration fails.', async () => { - const client = 'unknown-db'; - const expectedError = new Error(`Unsupported database client ${client}`); - const config = mockServices.rootConfig({ - data: { - backend: { - database: { - client, - }, - }, - }, - }); - const adapterFactory = new CasbinDBAdapterFactory(config, db); - - await expect(adapterFactory.createAdapter()).rejects.toStrictEqual( - expectedError, - ); - expect(newAdapterMock).not.toHaveBeenCalled(); - }); -}); diff --git a/workspaces/rbac/plugins/rbac-backend/src/database/casbin-adapter-factory.ts b/workspaces/rbac/plugins/rbac-backend/src/database/casbin-adapter-factory.ts deleted file mode 100644 index b8c00f6986e..00000000000 --- a/workspaces/rbac/plugins/rbac-backend/src/database/casbin-adapter-factory.ts +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import type { Config } from '@backstage/config'; -import type { ConfigApi } from '@backstage/core-plugin-api'; - -import { Knex } from 'knex'; -import TypeORMAdapter from 'typeorm-adapter'; - -import { resolve } from 'path'; -import type { ConnectionOptions, TlsOptions } from 'tls'; - -import '@backstage/backend-defaults/database'; - -const DEFAULT_SQLITE3_STORAGE_FILE_NAME = 'rbac.sqlite'; - -const UNSUPPORTED_PG_CONNECTION_STRING_ERROR = - 'Postgres connection config in string format is not supported yet, an object is expected'; - -export class CasbinDBAdapterFactory { - public constructor( - private readonly config: ConfigApi, - private readonly databaseClient: Knex, - ) {} - - public async createAdapter(): Promise { - const databaseConfig = this.config.getOptionalConfig('backend.database'); - const client = databaseConfig?.getOptionalString('client'); - - let adapter; - if (client === 'pg') { - const knexConnection = this.databaseClient.client.config.connection; - const resolved = await this.resolveKnexPgConnection(); - - if (!resolved.database) { - throw new Error('missing database name on Knex connection'); - } - - const schema = - (await this.databaseClient.client.searchPath?.[0]) ?? 'public'; - - const ssl = this.handlePostgresSSL(databaseConfig!); - - const password = - typeof knexConnection === 'function' - ? async () => { - const connection = await knexConnection(); - if (connection.password === undefined) { - throw new Error('missing password on resolved Knex connection'); - } - return String(connection.password); - } - : databaseConfig?.getOptionalString('connection.password'); - - adapter = await TypeORMAdapter.newAdapter({ - type: 'postgres', - host: databaseConfig?.getString('connection.host'), - port: databaseConfig?.getNumber('connection.port'), - username: databaseConfig?.getOptionalString('connection.user'), - password: password as any, // TypeORM types don't include function, but pg driver supports it - ssl, - database: resolved.database, - schema: schema, - poolSize: databaseConfig?.getOptionalNumber('knexConfig.pool.max'), - }); - } - - if (client === 'better-sqlite3') { - let storage; - if (typeof databaseConfig?.get('connection')?.valueOf() === 'string') { - storage = databaseConfig?.getString('connection'); - } else if (databaseConfig?.has('connection.directory')) { - const storageDir = databaseConfig?.getString('connection.directory'); - storage = resolve(storageDir, DEFAULT_SQLITE3_STORAGE_FILE_NAME); - } - - adapter = await TypeORMAdapter.newAdapter({ - type: 'better-sqlite3', - // Storage type or path to the storage. - database: storage || ':memory:', - }); - } - - if (!adapter) { - throw new Error(`Unsupported database client ${client}`); - } - - return adapter; - } - - private async resolveKnexPgConnection(): Promise { - const connection = this.databaseClient.client.config.connection; - - if (typeof connection === 'function') { - return await connection(); - } - - if (typeof connection === 'string' || connection instanceof String) { - throw new Error(UNSUPPORTED_PG_CONNECTION_STRING_ERROR); - } - - return connection; - } - - private handlePostgresSSL( - dbConfig: Config, - ): boolean | TlsOptions | undefined { - const connection = dbConfig.getOptional( - 'connection', - ); - if (!connection) { - return undefined; - } - - if (typeof connection === 'string' || connection instanceof String) { - throw new Error(UNSUPPORTED_PG_CONNECTION_STRING_ERROR); - } - - const ssl: boolean | ConnectionOptions | undefined = connection.ssl; - - if (ssl === undefined) { - return undefined; - } - - if (typeof ssl === 'boolean') { - return ssl; - } - - if (typeof ssl === 'object') { - const { ca, rejectUnauthorized } = ssl as ConnectionOptions; - const tlsOpts = { ca, rejectUnauthorized }; - - // SSL object was defined with some options that we don't support yet. - if (Object.values(tlsOpts).every(el => el === undefined)) { - return true; - } - - return tlsOpts; - } - - return undefined; - } -} diff --git a/workspaces/rbac/plugins/rbac-backend/src/database/casbin-knex-adapter.test.ts b/workspaces/rbac/plugins/rbac-backend/src/database/casbin-knex-adapter.test.ts new file mode 100644 index 00000000000..e4e371b64bb --- /dev/null +++ b/workspaces/rbac/plugins/rbac-backend/src/database/casbin-knex-adapter.test.ts @@ -0,0 +1,475 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Helper, newModelFromString } from 'casbin'; +import knex, { Knex } from 'knex'; + +import { CasbinKnexAdapter } from './casbin-knex-adapter'; + +const MODEL = ` +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act, eft + +[policy_effect] +e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) + +[role_definition] +g = _, _ + +[matchers] +m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act +`; + +const TABLE = 'casbin_rule'; + +async function createTable(db: Knex) { + await db.schema.createTable(TABLE, table => { + table.increments('id').primary(); + table.string('ptype').nullable(); + table.string('v0').nullable(); + table.string('v1').nullable(); + table.string('v2').nullable(); + table.string('v3').nullable(); + table.string('v4').nullable(); + table.string('v5').nullable(); + }); +} + +async function seedRules( + db: Knex, + rows: Array<{ + ptype: string; + v0?: string; + v1?: string; + v2?: string; + v3?: string; + v4?: string; + v5?: string; + }>, +) { + await db(TABLE).insert( + rows.map(r => ({ + ptype: r.ptype, + v0: r.v0 ?? null, + v1: r.v1 ?? null, + v2: r.v2 ?? null, + v3: r.v3 ?? null, + v4: r.v4 ?? null, + v5: r.v5 ?? null, + })), + ); +} + +async function allRows(db: Knex) { + return db(TABLE).select('*').orderBy('id'); +} + +describe('CasbinKnexAdapter', () => { + let db: Knex; + let adapter: CasbinKnexAdapter; + + beforeAll(async () => { + db = knex({ + client: 'better-sqlite3', + connection: ':memory:', + useNullAsDefault: true, + }); + }); + + afterAll(async () => { + await db.destroy(); + }); + + beforeEach(async () => { + if (await db.schema.hasTable(TABLE)) { + await db.schema.dropTable(TABLE); + } + await createTable(db); + adapter = await CasbinKnexAdapter.newAdapter(db); + }); + + describe('isFiltered', () => { + it('should return false initially', () => { + expect(adapter.isFiltered()).toBe(false); + }); + + it('should return true after loadFilteredPolicy', async () => { + await adapter.loadFilteredPolicy(newModelFromString(MODEL), [ + { ptype: 'p' }, + ]); + expect(adapter.isFiltered()).toBe(true); + }); + + it('should reset to false after loadPolicy', async () => { + await adapter.loadFilteredPolicy(newModelFromString(MODEL), [ + { ptype: 'p' }, + ]); + await adapter.loadPolicy(newModelFromString(MODEL)); + expect(adapter.isFiltered()).toBe(false); + }); + }); + + describe('loadPolicy', () => { + it('should load nothing from empty table', async () => { + const model = newModelFromString(MODEL); + await adapter.loadPolicy(model); + expect(model.getPolicy('p', 'p')).toEqual([]); + expect(model.getPolicy('g', 'g')).toEqual([]); + }); + + it('should load p-type policies', async () => { + await seedRules(db, [ + { ptype: 'p', v0: 'alice', v1: 'data1', v2: 'read', v3: 'allow' }, + { ptype: 'p', v0: 'bob', v1: 'data1', v2: 'write', v3: 'deny' }, + ]); + + const model = newModelFromString(MODEL); + await adapter.loadPolicy(model); + expect(model.getPolicy('p', 'p')).toHaveLength(2); + }); + + it('should load g-type policies', async () => { + await seedRules(db, [{ ptype: 'g', v0: 'alice', v1: 'role:admin' }]); + + const model = newModelFromString(MODEL); + await adapter.loadPolicy(model); + expect(model.getPolicy('g', 'g')).toHaveLength(1); + expect(model.getPolicy('g', 'g')[0]).toEqual(['alice', 'role:admin']); + }); + + it('should load both p and g policies', async () => { + await seedRules(db, [ + { ptype: 'p', v0: 'alice', v1: 'data1', v2: 'read', v3: 'allow' }, + { ptype: 'g', v0: 'alice', v1: 'role:admin' }, + ]); + + const model = newModelFromString(MODEL); + await adapter.loadPolicy(model); + expect(model.getPolicy('p', 'p')).toHaveLength(1); + expect(model.getPolicy('g', 'g')).toHaveLength(1); + }); + }); + + describe('loadFilteredPolicy', () => { + beforeEach(async () => { + await seedRules(db, [ + { ptype: 'p', v0: 'alice', v1: 'data1', v2: 'read', v3: 'allow' }, + { ptype: 'p', v0: 'bob', v1: 'data1', v2: 'write', v3: 'deny' }, + { ptype: 'p', v0: 'admin', v1: 'data2', v2: 'read', v3: 'allow' }, + { ptype: 'g', v0: 'alice', v1: 'role:admin' }, + { ptype: 'g', v0: 'bob', v1: 'role:viewer' }, + ]); + }); + + it('should filter by ptype', async () => { + const model = newModelFromString(MODEL); + await adapter.loadFilteredPolicy(model, [{ ptype: 'p' }]); + expect(model.getPolicy('p', 'p')).toHaveLength(3); + expect(model.getPolicy('g', 'g')).toHaveLength(0); + }); + + it('should filter by ptype and v0', async () => { + const model = newModelFromString(MODEL); + await adapter.loadFilteredPolicy(model, [{ ptype: 'p', v0: 'alice' }]); + expect(model.getPolicy('p', 'p')).toHaveLength(1); + expect(model.getPolicy('p', 'p')[0][0]).toBe('alice'); + }); + + it('should OR multiple filter objects', async () => { + const model = newModelFromString(MODEL); + await adapter.loadFilteredPolicy(model, [ + { ptype: 'p', v0: 'alice' }, + { ptype: 'p', v0: 'bob' }, + ]); + expect(model.getPolicy('p', 'p')).toHaveLength(2); + }); + + it('should load all on empty filter', async () => { + const model = newModelFromString(MODEL); + await adapter.loadFilteredPolicy(model, []); + expect(model.getPolicy('p', 'p')).toHaveLength(3); + expect(model.getPolicy('g', 'g')).toHaveLength(2); + }); + + it('should set isFiltered to true', async () => { + const model = newModelFromString(MODEL); + await adapter.loadFilteredPolicy(model, [{ ptype: 'p' }]); + expect(adapter.isFiltered()).toBe(true); + }); + }); + + describe('savePolicy', () => { + it('should save model to database', async () => { + const model = newModelFromString(MODEL); + Helper.loadPolicyLine('p, alice, data1, read, allow', model); + Helper.loadPolicyLine('g, alice, role:admin', model); + + const result = await adapter.savePolicy(model); + expect(result).toBe(true); + + const rows = await allRows(db); + expect(rows).toHaveLength(2); + }); + + it('should replace existing rules', async () => { + await seedRules(db, [ + { ptype: 'p', v0: 'old', v1: 'data', v2: 'read', v3: 'allow' }, + ]); + + const model = newModelFromString(MODEL); + Helper.loadPolicyLine('p, new, data, write, deny', model); + await adapter.savePolicy(model); + + const rows = await allRows(db); + expect(rows).toHaveLength(1); + expect(rows[0].v0).toBe('new'); + }); + + it('should clear table for empty model', async () => { + await seedRules(db, [ + { ptype: 'p', v0: 'alice', v1: 'data1', v2: 'read', v3: 'allow' }, + ]); + + await adapter.savePolicy(newModelFromString(MODEL)); + expect(await allRows(db)).toHaveLength(0); + }); + }); + + describe('addPolicy', () => { + it('should insert a policy', async () => { + await adapter.addPolicy('p', 'p', ['alice', 'data1', 'read', 'allow']); + const rows = await allRows(db); + expect(rows).toHaveLength(1); + expect(rows[0].ptype).toBe('p'); + expect(rows[0].v0).toBe('alice'); + }); + + it('should insert a grouping policy', async () => { + await adapter.addPolicy('g', 'g', ['alice', 'role:admin']); + const rows = await allRows(db); + expect(rows).toHaveLength(1); + expect(rows[0].ptype).toBe('g'); + }); + + it('should store null for missing trailing values', async () => { + await adapter.addPolicy('g', 'g', ['alice', 'role:viewer']); + const rows = await allRows(db); + expect(rows[0].v2).toBeNull(); + expect(rows[0].v5).toBeNull(); + }); + }); + + describe('addPolicies', () => { + it('should batch insert', async () => { + await adapter.addPolicies('p', 'p', [ + ['alice', 'data1', 'read', 'allow'], + ['bob', 'data1', 'write', 'deny'], + ]); + expect(await allRows(db)).toHaveLength(2); + }); + + it('should handle empty array', async () => { + await adapter.addPolicies('p', 'p', []); + expect(await allRows(db)).toHaveLength(0); + }); + }); + + describe('removePolicy', () => { + beforeEach(async () => { + await seedRules(db, [ + { ptype: 'p', v0: 'alice', v1: 'data1', v2: 'read', v3: 'allow' }, + { ptype: 'p', v0: 'bob', v1: 'data1', v2: 'write', v3: 'deny' }, + { ptype: 'g', v0: 'alice', v1: 'role:admin' }, + ]); + }); + + it('should remove matching rule', async () => { + await adapter.removePolicy('p', 'p', ['alice', 'data1', 'read', 'allow']); + const rows = await allRows(db); + expect(rows).toHaveLength(2); + expect( + rows.find(r => r.v0 === 'alice' && r.ptype === 'p'), + ).toBeUndefined(); + }); + + it('should not remove when no match', async () => { + await adapter.removePolicy('p', 'p', [ + 'nonexistent', + 'data1', + 'read', + 'allow', + ]); + expect(await allRows(db)).toHaveLength(3); + }); + }); + + describe('removePolicies', () => { + beforeEach(async () => { + await seedRules(db, [ + { ptype: 'p', v0: 'alice', v1: 'data1', v2: 'read', v3: 'allow' }, + { ptype: 'p', v0: 'bob', v1: 'data1', v2: 'write', v3: 'deny' }, + ]); + }); + + it('should batch remove', async () => { + await adapter.removePolicies('p', 'p', [ + ['alice', 'data1', 'read', 'allow'], + ['bob', 'data1', 'write', 'deny'], + ]); + expect(await allRows(db)).toHaveLength(0); + }); + + it('should handle empty array', async () => { + await adapter.removePolicies('p', 'p', []); + expect(await allRows(db)).toHaveLength(2); + }); + }); + + describe('removeFilteredPolicy', () => { + beforeEach(async () => { + await seedRules(db, [ + { ptype: 'p', v0: 'alice', v1: 'data1', v2: 'read', v3: 'allow' }, + { ptype: 'p', v0: 'alice', v1: 'data1', v2: 'write', v3: 'deny' }, + { ptype: 'p', v0: 'bob', v1: 'data1', v2: 'read', v3: 'allow' }, + { ptype: 'g', v0: 'alice', v1: 'role:admin' }, + ]); + }); + + it('should remove by fieldIndex=0', async () => { + await adapter.removeFilteredPolicy('p', 'p', 0, 'alice'); + const rows = (await allRows(db)).filter((r: any) => r.ptype === 'p'); + expect(rows).toHaveLength(1); + expect(rows[0].v0).toBe('bob'); + }); + + it('should remove by multiple field values', async () => { + await adapter.removeFilteredPolicy('p', 'p', 0, 'alice', 'data1', 'read'); + const rows = (await allRows(db)).filter((r: any) => r.ptype === 'p'); + expect(rows).toHaveLength(2); + }); + + it('should skip empty field values', async () => { + await adapter.removeFilteredPolicy('p', 'p', 0, '', 'data1'); + const rows = (await allRows(db)).filter((r: any) => r.ptype === 'p'); + expect(rows).toHaveLength(0); + }); + + it('should not affect other ptypes', async () => { + await adapter.removeFilteredPolicy('p', 'p', 0, 'alice'); + const gRows = (await allRows(db)).filter((r: any) => r.ptype === 'g'); + expect(gRows).toHaveLength(1); + }); + }); + + describe('round-trip', () => { + it('should add then load', async () => { + await adapter.addPolicy('p', 'p', ['alice', 'data1', 'read', 'allow']); + + const model = newModelFromString(MODEL); + await adapter.loadPolicy(model); + expect(model.getPolicy('p', 'p')).toEqual([ + ['alice', 'data1', 'read', 'allow'], + ]); + }); + + it('should savePolicy then loadPolicy', async () => { + const saveModel = newModelFromString(MODEL); + Helper.loadPolicyLine('p, admin, data1, read, allow', saveModel); + Helper.loadPolicyLine('g, alice, role:admin', saveModel); + await adapter.savePolicy(saveModel); + + const loadModel = newModelFromString(MODEL); + await adapter.loadPolicy(loadModel); + expect(loadModel.getPolicy('p', 'p')).toHaveLength(1); + expect(loadModel.getPolicy('g', 'g')).toHaveLength(1); + }); + + it('should add, filter, remove, verify', async () => { + await adapter.addPolicies('p', 'p', [ + ['alice', 'data1', 'read', 'allow'], + ['bob', 'data1', 'write', 'deny'], + ]); + await adapter.addPolicy('g', 'g', ['alice', 'role:admin']); + + const filtered = newModelFromString(MODEL); + await adapter.loadFilteredPolicy(filtered, [{ ptype: 'p', v0: 'alice' }]); + expect(filtered.getPolicy('p', 'p')).toHaveLength(1); + + await adapter.removePolicy('p', 'p', ['alice', 'data1', 'read', 'allow']); + + const after = newModelFromString(MODEL); + await adapter.loadPolicy(after); + expect(after.getPolicy('p', 'p')).toHaveLength(1); + expect(after.getPolicy('g', 'g')).toHaveLength(1); + }); + }); + + describe('runWithTransaction', () => { + it('reuses an open transaction instead of acquiring another pool connection', async () => { + // Mirrors EnforcerDelegate holding a trx while Casbin auto-saves. + // With pool max=1 this deadlocks unless the adapter joins the trx. + const limitedDb = knex({ + client: 'better-sqlite3', + connection: ':memory:', + useNullAsDefault: true, + pool: { min: 1, max: 1 }, + acquireConnectionTimeout: 2_000, + }); + await createTable(limitedDb); + const limitedAdapter = await CasbinKnexAdapter.newAdapter(limitedDb); + + const trx = await limitedDb.transaction(); + expect(limitedAdapter.isSameClient(trx)).toBe(true); + try { + await limitedAdapter.runWithTransaction(trx, async () => { + await limitedAdapter.addPolicies('p', 'p', [ + ['alice', 'data1', 'read', 'allow'], + ['bob', 'data2', 'write', 'allow'], + ]); + const model = newModelFromString(MODEL); + await limitedAdapter.loadPolicy(model); + expect(model.getPolicy('p', 'p')).toHaveLength(2); + }); + await trx.commit(); + } catch (err) { + await trx.rollback(); + throw err; + } finally { + await limitedDb.destroy(); + } + }); + + it('does not treat a transaction from another knex instance as same client', async () => { + const other = knex({ + client: 'better-sqlite3', + connection: ':memory:', + useNullAsDefault: true, + }); + const trx = await other.transaction(); + try { + expect(adapter.isSameClient(trx)).toBe(false); + await trx.commit(); + } catch (err) { + await trx.rollback(); + throw err; + } finally { + await other.destroy(); + } + }); + }); +}); diff --git a/workspaces/rbac/plugins/rbac-backend/src/database/casbin-knex-adapter.ts b/workspaces/rbac/plugins/rbac-backend/src/database/casbin-knex-adapter.ts new file mode 100644 index 00000000000..346fb4507d0 --- /dev/null +++ b/workspaces/rbac/plugins/rbac-backend/src/database/casbin-knex-adapter.ts @@ -0,0 +1,289 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import type { BatchAdapter, FilteredAdapter, Model } from 'casbin'; +import { knex as knexFactory, type Knex } from 'knex'; + +const TABLE_NAME = 'casbin_rule'; + +const POLICY_COLUMNS = ['ptype', 'v0', 'v1', 'v2', 'v3', 'v4', 'v5'] as const; + +const SELECT_COLS = POLICY_COLUMNS.join(', '); +const SELECT_ALL = `SELECT ${SELECT_COLS} FROM "${TABLE_NAME}"`; + +interface CasbinRule { + ptype: string; + v0: string | null; + v1: string | null; + v2: string | null; + v3: string | null; + v4: string | null; + v5: string | null; +} + +export type CasbinPolicyFilter = Array>; + +function loadRowIntoModel(row: CasbinRule, model: Model): void { + const sec = row.ptype.substring(0, 1); + const ast = model.model.get(sec)?.get(row.ptype); + if (!ast) return; + + let len: number; + if (row.v5) len = 6; + else if (row.v4) len = 5; + else if (row.v3) len = 4; + else if (row.v2) len = 3; + else if (row.v1) len = 2; + else if (row.v0) len = 1; + else return; + + const rule: string[] = new Array(len); + if (len >= 1) rule[0] = row.v0 ?? ''; + if (len >= 2) rule[1] = row.v1 ?? ''; + if (len >= 3) rule[2] = row.v2 ?? ''; + if (len >= 4) rule[3] = row.v3 ?? ''; + if (len >= 5) rule[4] = row.v4 ?? ''; + if (len >= 6) rule[5] = row.v5 ?? ''; + + ast.policy.push(rule); +} + +function ruleToRow(ptype: string, rule: string[]): CasbinRule { + return { + ptype, + v0: rule[0] ?? null, + v1: rule[1] ?? null, + v2: rule[2] ?? null, + v3: rule[3] ?? null, + v4: rule[4] ?? null, + v5: rule[5] ?? null, + }; +} + +export class CasbinKnexAdapter implements FilteredAdapter, BatchAdapter { + private filtered = false; + private activeTrx?: Knex.Transaction; + private dedicatedPool?: Knex; + + private readonly sqlCache = new Map(); + + private constructor(private readonly knex: Knex) {} + + static async newAdapter(knex: Knex): Promise { + return new CasbinKnexAdapter(knex); + } + + static async newAdapterWithDedicatedPool( + parentKnex: Knex, + poolConfig?: { min?: number; max?: number }, + ): Promise { + const cfg = parentKnex.client.config; + const dedicatedKnex = knexFactory({ + client: cfg.client, + connection: cfg.connection, + pool: poolConfig ?? { min: 2, max: 10 }, + searchPath: cfg.searchPath, + }); + const adapter = new CasbinKnexAdapter(dedicatedKnex); + adapter.dedicatedPool = dedicatedKnex; + return adapter; + } + + async destroy(): Promise { + if (this.dedicatedPool) { + await this.dedicatedPool.destroy(); + this.dedicatedPool = undefined; + } + } + + async runWithTransaction( + trx: Knex.Transaction, + fn: () => Promise, + ): Promise { + const previous = this.activeTrx; + this.activeTrx = trx; + try { + return await fn(); + } finally { + this.activeTrx = previous; + } + } + + isSameClient(trx: Knex.Transaction): boolean { + return trx.client.config === this.knex.client.config; + } + + private db(): Knex | Knex.Transaction { + return this.activeTrx ?? this.knex; + } + + isFiltered(): boolean { + return this.filtered; + } + + async loadPolicy(model: Model): Promise { + const result = await this.db().raw(SELECT_ALL); + const rows: CasbinRule[] = Array.isArray(result) ? result : result.rows; + for (const row of rows) { + loadRowIntoModel(row, model); + } + this.filtered = false; + } + + async loadFilteredPolicy( + model: Model, + filter: CasbinPolicyFilter, + ): Promise { + if (!filter || filter.length === 0) { + await this.loadPolicy(model); + return; + } + + const sql = this.getFilterSQL(filter); + const bindings = this.getFilterBindings(filter); + const result = await this.db().raw(sql, bindings); + const rows: CasbinRule[] = Array.isArray(result) ? result : result.rows; + + for (const row of rows) { + loadRowIntoModel(row, model); + } + this.filtered = true; + } + + private getFilterSQL(filter: CasbinPolicyFilter): string { + const cacheKey = this.buildCacheKey(filter); + let sql = this.sqlCache.get(cacheKey); + if (sql !== undefined) return sql; + + if (filter.length === 1) { + const keys = Object.keys(filter[0]).sort(); + sql = `${SELECT_ALL} WHERE ${keys.map(k => `"${k}" = ?`).join(' AND ')}`; + } else { + const groups = filter.map(f => { + const keys = Object.keys(f).sort(); + return `(${keys.map(k => `"${k}" = ?`).join(' AND ')})`; + }); + sql = `${SELECT_ALL} WHERE ${groups.join(' OR ')}`; + } + + this.sqlCache.set(cacheKey, sql); + return sql; + } + + private buildCacheKey(filter: CasbinPolicyFilter): string { + return filter.map(f => Object.keys(f).sort().join(',')).join('|'); + } + + private getFilterBindings(filter: CasbinPolicyFilter): unknown[] { + const bindings: unknown[] = []; + for (const f of filter) { + for (const key of Object.keys(f).sort()) { + bindings.push((f as Record)[key]); + } + } + return bindings; + } + + async savePolicy(model: Model): Promise { + const rows: CasbinRule[] = []; + + for (const [, policyMap] of model.model) { + for (const [ptype, assertion] of policyMap) { + for (const rule of assertion.policy) { + rows.push(ruleToRow(ptype, rule)); + } + } + } + + await this.runInTransaction(async trx => { + await trx(TABLE_NAME).del(); + if (rows.length > 0) { + await trx.batchInsert(TABLE_NAME, rows, 50); + } + }); + + return true; + } + + async addPolicy(_sec: string, ptype: string, rule: string[]): Promise { + await this.db()(TABLE_NAME).insert(ruleToRow(ptype, rule)); + } + + async addPolicies( + _sec: string, + ptype: string, + rules: string[][], + ): Promise { + if (rules.length === 0) return; + const rows = rules.map(rule => ruleToRow(ptype, rule)); + await this.runInTransaction(async trx => { + await trx.batchInsert(TABLE_NAME, rows, 50); + }); + } + + async removePolicy( + _sec: string, + ptype: string, + rule: string[], + ): Promise { + const where: Record = { ptype }; + for (let i = 0; i < rule.length; i++) { + where[`v${i}`] = rule[i]; + } + await this.db()(TABLE_NAME).where(where).del(); + } + + async removePolicies( + _sec: string, + ptype: string, + rules: string[][], + ): Promise { + if (rules.length === 0) return; + await this.runInTransaction(async trx => { + for (const rule of rules) { + const where: Record = { ptype }; + for (let i = 0; i < rule.length; i++) { + where[`v${i}`] = rule[i]; + } + await trx(TABLE_NAME).where(where).del(); + } + }); + } + + async removeFilteredPolicy( + _sec: string, + ptype: string, + fieldIndex: number, + ...fieldValues: string[] + ): Promise { + const where: Record = { ptype }; + for (let i = 0; i < fieldValues.length; i++) { + if (fieldValues[i] !== '') { + where[`v${fieldIndex + i}`] = fieldValues[i]; + } + } + await this.db()(TABLE_NAME).where(where).del(); + } + + private async runInTransaction( + fn: (trx: Knex.Transaction) => Promise, + ): Promise { + if (this.activeTrx) { + await fn(this.activeTrx); + return; + } + await this.knex.transaction(fn); + } +} diff --git a/workspaces/rbac/plugins/rbac-backend/src/database/conditional-storage.test.ts b/workspaces/rbac/plugins/rbac-backend/src/database/conditional-storage.test.ts index 07aa85648ab..c7ff4554a8a 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/database/conditional-storage.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/database/conditional-storage.test.ts @@ -106,7 +106,7 @@ describe('DataBaseConditionalStorage', () => { migrations: { skip: false }, }); - await migrate(mockDatabaseService); + await migrate(mockDatabaseService, knex); return { knex, db: new DataBaseConditionalStorage(knex), diff --git a/workspaces/rbac/plugins/rbac-backend/src/database/extra-permission-enabled-plugins-storage.test.ts b/workspaces/rbac/plugins/rbac-backend/src/database/extra-permission-enabled-plugins-storage.test.ts index 461085adeaa..df914a14131 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/database/extra-permission-enabled-plugins-storage.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/database/extra-permission-enabled-plugins-storage.test.ts @@ -38,7 +38,7 @@ describe('PermissionDependentPluginDatabaseStore', () => { migrations: { skip: false }, }); - await migrate(mockDatabaseService); + await migrate(mockDatabaseService, knex); return { knex, db: new PermissionDependentPluginDatabaseStore(knex), diff --git a/workspaces/rbac/plugins/rbac-backend/src/database/migration.ts b/workspaces/rbac/plugins/rbac-backend/src/database/migration.ts index 615eec99add..bc9931abf3b 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/database/migration.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/database/migration.ts @@ -17,15 +17,14 @@ import { DatabaseService, resolvePackagePath, } from '@backstage/backend-plugin-api'; +import type { Knex } from 'knex'; const migrationsDir = resolvePackagePath( - '@backstage-community/plugin-rbac-backend', // Package name - 'migrations', // Migrations directory + '@backstage-community/plugin-rbac-backend', + 'migrations', ); -export async function migrate(databaseManager: DatabaseService) { - const knex = await databaseManager.getClient(); - +export async function migrate(databaseManager: DatabaseService, knex: Knex) { if (!databaseManager.migrations?.skip) { await knex.migrate.latest({ directory: migrationsDir, diff --git a/workspaces/rbac/plugins/rbac-backend/src/database/role-metadata.test.ts b/workspaces/rbac/plugins/rbac-backend/src/database/role-metadata.test.ts index 2ddb690cda4..d54411def74 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/database/role-metadata.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/database/role-metadata.test.ts @@ -45,7 +45,7 @@ describe('role-metadata-db-table', () => { migrations: { skip: false }, }); - await migrate(mockDatabaseService); + await migrate(mockDatabaseService, knex); const config = mockServices.rootConfig(); return { knex, @@ -61,7 +61,7 @@ describe('role-metadata-db-table', () => { migrations: { skip: false }, }); - await migrate(mockDatabaseService); + await migrate(mockDatabaseService, knex); const config = mockServices.rootConfig({ data: { permission: { diff --git a/workspaces/rbac/plugins/rbac-backend/src/file-permissions/csv-file-watcher.test.ts b/workspaces/rbac/plugins/rbac-backend/src/file-permissions/csv-file-watcher.test.ts index a0c9de504a7..2ccb7bafdc3 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/file-permissions/csv-file-watcher.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/file-permissions/csv-file-watcher.test.ts @@ -32,7 +32,7 @@ import type { Source } from '@backstage-community/plugin-rbac-common'; import { resolve } from 'path'; import { ADMIN_ROLE_AUTHOR } from '../admin-permissions/admin-creation'; -import { CasbinDBAdapterFactory } from '../database/casbin-adapter-factory'; +import { CasbinKnexAdapter } from '../database/casbin-knex-adapter'; import { RoleMetadataDao, RoleMetadataStorage, @@ -41,6 +41,7 @@ import { BackstageRoleManager } from '../role-manager/role-manager'; import { DefaultPermissionsReader } from '../default-permissions/default-permissions'; import { EnforcerDelegate } from '../service/enforcer-delegate'; import { MODEL } from '../service/permission-model'; +import { createTestCasbinKnex } from '../../__fixtures__/mock-utils'; import { CSVFileWatcher } from './csv-file-watcher'; import { mockAuditorService } from '../../__fixtures__/mock-utils'; import { conditionalStorageMock } from '../../__fixtures__/mock-utils'; @@ -120,8 +121,6 @@ const roleMetadataStorageMock: RoleMetadataStorage = { syncDefaultRoleMetadata: jest.fn().mockResolvedValue(undefined), }; -const mockClientKnex = Knex.knex({ client: MockClient }); - const mockAuthService = mockServices.auth(); const currentPermissionPolicies = [ @@ -154,12 +153,8 @@ describe('CSVFileWatcher', () => { '../../__fixtures__/data/valid-csv/rbac-policy.csv', ); - const config = newConfig(); - - const adapter = await new CasbinDBAdapterFactory( - config, - mockClientKnex, - ).createAdapter(); + const casbinKnex = await createTestCasbinKnex(); + const adapter = await CasbinKnexAdapter.newAdapter(casbinKnex); const stringModel = newModelFromString(MODEL); const enf = await createEnforcer(stringModel, adapter, mockLoggerService); diff --git a/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.hierarchy.test.ts b/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.hierarchy.test.ts index 22dd318cd1b..dc76fce2f4b 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.hierarchy.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.hierarchy.test.ts @@ -40,6 +40,7 @@ import { resolve } from 'path'; import { mockAuditorService, conditionalStorageMock, + createTestCasbinKnex, csvPermFile, mockAuthService, mockClientKnex, @@ -48,7 +49,7 @@ import { catalogMock, mockUserInfoService, } from '../../__fixtures__/mock-utils'; -import { CasbinDBAdapterFactory } from '../database/casbin-adapter-factory'; +import { CasbinKnexAdapter } from '../database/casbin-knex-adapter'; import { RoleMetadataStorage } from '../database/role-metadata'; import { BackstageRoleManager } from '../role-manager/role-manager'; import { DefaultPermissionsReader } from '../default-permissions/default-permissions'; @@ -1037,11 +1038,9 @@ function newConfig(permFile?: string): Config { }); } -async function newAdapter(config: Config): Promise { - return await new CasbinDBAdapterFactory( - config, - mockClientKnex, - ).createAdapter(); +async function newAdapter(_config: Config): Promise { + const casbinKnex = await createTestCasbinKnex(); + return await CasbinKnexAdapter.newAdapter(casbinKnex); } async function createEnforcer( diff --git a/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.test.ts b/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.test.ts index 21d4e939d75..06634ba0ce9 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/policies/permission-policy.test.ts @@ -42,8 +42,12 @@ import type { import { resolve } from 'path'; +import { + createTestCasbinKnex, + mockClientKnex, +} from '../../__fixtures__/mock-utils'; import { ADMIN_ROLE_NAME } from '../admin-permissions/admin-creation'; -import { CasbinDBAdapterFactory } from '../database/casbin-adapter-factory'; +import { CasbinKnexAdapter } from '../database/casbin-knex-adapter'; import { ConditionalStorage } from '../database/conditional-storage'; import { RoleMetadataDao, @@ -104,8 +108,6 @@ const csvPermFile = resolve( '../../__fixtures__/data/valid-csv/rbac-policy.csv', ); -const mockClientKnex = Knex.knex({ client: MockClient }); - const pluginMetadataCollectorMock: Partial = { getPluginConditionRules: jest.fn().mockImplementation(), @@ -2442,11 +2444,9 @@ function newConfigWithDefaultRole( }); } -async function newAdapter(config: Config): Promise { - return await new CasbinDBAdapterFactory( - config, - mockClientKnex, - ).createAdapter(); +async function newAdapter(_config: Config): Promise { + const casbinKnex = await createTestCasbinKnex(); + return await CasbinKnexAdapter.newAdapter(casbinKnex); } async function createEnforcer( diff --git a/workspaces/rbac/plugins/rbac-backend/src/providers/connect-providers.test.ts b/workspaces/rbac/plugins/rbac-backend/src/providers/connect-providers.test.ts index dacfc7dfe76..3f9ba1b913d 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/providers/connect-providers.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/providers/connect-providers.test.ts @@ -31,7 +31,7 @@ import type { RBACProviderConnection, } from '@backstage-community/plugin-rbac-node'; -import { CasbinDBAdapterFactory } from '../database/casbin-adapter-factory'; +import { CasbinKnexAdapter } from '../database/casbin-knex-adapter'; import { RoleMetadataDao, RoleMetadataStorage, @@ -40,6 +40,7 @@ import { BackstageRoleManager } from '../role-manager/role-manager'; import { DefaultPermissionsReader } from '../default-permissions/default-permissions'; import { EnforcerDelegate } from '../service/enforcer-delegate'; import { MODEL } from '../service/permission-model'; +import { createTestCasbinKnex } from '../../__fixtures__/mock-utils'; import { Connection, connectRBACProviders } from './connect-providers'; import { catalogMock, @@ -127,8 +128,6 @@ const roleMetadataStorageMock: RoleMetadataStorage = { const mockAuthService = mockServices.auth(); -const mockClientKnex = Knex.knex({ client: MockClient }); - const providerMock: RBACProvider = { getProviderName: jest.fn().mockImplementation(), connect: jest.fn().mockImplementation(), @@ -219,10 +218,8 @@ describe('Connection', () => { beforeEach(async () => { const id = 'test'; - const adapter = await new CasbinDBAdapterFactory( - config, - mockClientKnex, - ).createAdapter(); + const casbinKnex = await createTestCasbinKnex(); + const adapter = await CasbinKnexAdapter.newAdapter(casbinKnex); const stringModel = newModelFromString(MODEL); const enf = await createEnforcer(stringModel, adapter, mockLoggerService); @@ -924,10 +921,8 @@ describe('connectRBACProviders', () => { it('should initialize rbac providers', async () => { connectSpy = jest.spyOn(providerMock, 'connect'); - const adapter = await new CasbinDBAdapterFactory( - config, - mockClientKnex, - ).createAdapter(); + const casbinKnex = await createTestCasbinKnex(); + const adapter = await CasbinKnexAdapter.newAdapter(casbinKnex); const stringModel = newModelFromString(MODEL); const enf = await createEnforcer(stringModel, adapter, mockLoggerService); diff --git a/workspaces/rbac/plugins/rbac-backend/src/service/enforcer-delegate.test.ts b/workspaces/rbac/plugins/rbac-backend/src/service/enforcer-delegate.test.ts index f90a78ba5ef..b8a7bb260ea 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/service/enforcer-delegate.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/service/enforcer-delegate.test.ts @@ -19,7 +19,7 @@ import { Enforcer, Model, newEnforcer, newModelFromString } from 'casbin'; import * as Knex from 'knex'; import { MockClient } from 'knex-mock-client'; -import { CasbinDBAdapterFactory } from '../database/casbin-adapter-factory'; +import { CasbinKnexAdapter } from '../database/casbin-knex-adapter'; import { RoleMetadataDao, RoleMetadataStorage, @@ -32,6 +32,7 @@ import { catalogMock, conditionalStorageMock, createEventMock, + createTestCasbinKnex, mockAuditorService, } from '../../__fixtures__/mock-utils'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; @@ -52,8 +53,6 @@ const roleMetadataStorageMock: RoleMetadataStorage = { syncDefaultRoleMetadata: jest.fn().mockResolvedValue(undefined), }; -const mockClientKnex = Knex.knex({ client: MockClient }); - const mockAuthService = mockServices.auth(); const config = mockServices.rootConfig({ @@ -143,10 +142,9 @@ describe('EnforcerDelegate', () => { const theModel = newModelFromString(MODEL); const logger = mockServices.logger.mock(); - const sqliteInMemoryAdapter = await new CasbinDBAdapterFactory( - config, - mockClientKnex, - ).createAdapter(); + const casbinKnex = await createTestCasbinKnex(); + const sqliteInMemoryAdapter = + await CasbinKnexAdapter.newAdapter(casbinKnex); adapterLoaderFilterGroupingPolicySpy = jest.spyOn( sqliteInMemoryAdapter, 'loadFilteredPolicy', diff --git a/workspaces/rbac/plugins/rbac-backend/src/service/enforcer-delegate.ts b/workspaces/rbac/plugins/rbac-backend/src/service/enforcer-delegate.ts index 443c3456bb3..ce4be413728 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/service/enforcer-delegate.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/service/enforcer-delegate.ts @@ -19,6 +19,8 @@ import { Knex } from 'knex'; import EventEmitter from 'events'; import { ADMIN_ROLE_NAME } from '../admin-permissions/admin-creation'; +import { CasbinKnexAdapter } from '../database/casbin-knex-adapter'; +import { ConditionalStorage } from '../database/conditional-storage'; import { RoleMetadataDao, RoleMetadataStorage, @@ -27,7 +29,6 @@ import { mergeRoleMetadata, policiesToString, policyToString } from '../helper'; import { MODEL } from './permission-model'; import { PoliciesData } from '../auditor/auditor'; import { AuditorService } from '@backstage/backend-plugin-api'; -import { ConditionalStorage } from '../database/conditional-storage'; export type RoleEvents = 'roleAdded'; export interface RoleEventEmitter { @@ -100,6 +101,38 @@ export class EnforcerDelegate implements RoleEventEmitter { return result; } + /** + * Reuses an open Knex transaction for Casbin adapter I/O when the transaction + * belongs to the same client as the adapter. Without this, EnforcerDelegate + * holds a pool connection while the knex adapter tries to acquire another — + * which deadlocks on SQLite (pool max=1). + * + * When the transaction is from a different Knex client (e.g. unit tests that + * mock role-metadata with knex-mock-client while Casbin uses SQLite), the + * adapter keeps its own connection — same separation TypeORM used to provide. + */ + private async withCasbinTransaction( + trx: Knex.Transaction | undefined, + fn: () => Promise, + ): Promise { + if (!trx) { + return fn(); + } + const adapter = this.enforcer.getAdapter(); + if (adapter instanceof CasbinKnexAdapter && adapter.isSameClient(trx)) { + return adapter.runWithTransaction(trx, fn); + } + return fn(); + } + + private async ensurePolicyLoaded(): Promise { + if (this.loadPolicyPromise) { + await this.loadPolicyPromise; + } else { + await this.loadPolicy(); + } + } + on(event: RoleEvents, listener: (role: string) => void): this { this.roleEventEmitter.on(event, listener); return this; @@ -205,60 +238,41 @@ export class EnforcerDelegate implements RoleEventEmitter { policy: string[], externalTrx?: Knex.Transaction, ): Promise { - const trx = externalTrx ?? (await this.knex.transaction()); - - if (await this.hasPolicy(...policy)) { - return; - } - try { + // Join before hasPolicy/adapter I/O when the caller already holds a trx. + await this.withCasbinTransaction(externalTrx, async () => { + if (await this.hasPolicy(...policy)) { + return; + } const ok = await this.enforcer.addPolicy(...policy); if (!ok) { throw new Error(`failed to create policy ${policyToString(policy)}`); } - if (!externalTrx) { - await trx.commit(); - } - } catch (err) { - if (!externalTrx) { - await trx.rollback(err); - } - throw err; - } + }); } async addPolicies( policies: string[][], externalTrx?: Knex.Transaction, ): Promise { - if (this.loadPolicyPromise) { - await this.loadPolicyPromise; - } else { - await this.loadPolicy(); + if (policies.length === 0) { + return; } - const addPoliciesOperation = (async () => { - if (policies.length === 0) { - return; - } - - const trx = externalTrx || (await this.knex.transaction()); + // Load outside the edit queue (avoids self-wait), but join externalTrx first + // so we don't acquire a second pool connection. + await this.withCasbinTransaction(externalTrx, () => + this.ensurePolicyLoaded(), + ); - try { + const addPoliciesOperation = (async () => { + await this.withCasbinTransaction(externalTrx, async () => { const ok = await this.enforcer.addPolicies(policies); if (!ok) { throw new Error( `Failed to store policies ${policiesToString(policies)}`, ); } - if (!externalTrx) { - await trx.commit(); - } - } catch (err) { - if (!externalTrx) { - await trx.rollback(err); - } - throw err; - } + }); })(); await this.execOperation(addPoliciesOperation); } @@ -268,51 +282,56 @@ export class EnforcerDelegate implements RoleEventEmitter { roleMetadata: RoleMetadataDao, externalTrx?: Knex.Transaction, ): Promise { - if (this.loadPolicyPromise) { - await this.loadPolicyPromise; - } else { - await this.loadPolicy(); - } + await this.withCasbinTransaction(externalTrx, () => + this.ensurePolicyLoaded(), + ); const addGroupingPolicyOperation = (async () => { const trx = externalTrx ?? (await this.knex.transaction()); - const entityRef = roleMetadata.roleEntityRef; - - if (await this.hasGroupingPolicy(...policy)) { - return; - } try { - let currentMetadata; - if (entityRef.startsWith(`role:`)) { - currentMetadata = await this.roleMetadataStorage.findRoleMetadata( - entityRef, - trx, - ); - } + await this.withCasbinTransaction(trx, async () => { + if (await this.hasGroupingPolicy(...policy)) { + return; + } - if (currentMetadata) { - await this.roleMetadataStorage.updateRoleMetadata( - mergeRoleMetadata(currentMetadata, roleMetadata), - entityRef, - trx, - ); - } else { - const currentDate: Date = new Date(); - roleMetadata.createdAt = currentDate.toUTCString(); - roleMetadata.lastModified = currentDate.toUTCString(); - await this.roleMetadataStorage.createRoleMetadata(roleMetadata, trx); - } + const entityRef = roleMetadata.roleEntityRef; + let currentMetadata; + if (entityRef.startsWith(`role:`)) { + currentMetadata = await this.roleMetadataStorage.findRoleMetadata( + entityRef, + trx, + ); + } - const ok = await this.enforcer.addGroupingPolicy(...policy); - if (!ok) { - throw new Error(`failed to create policy ${policyToString(policy)}`); - } + if (currentMetadata) { + await this.roleMetadataStorage.updateRoleMetadata( + mergeRoleMetadata(currentMetadata, roleMetadata), + entityRef, + trx, + ); + } else { + const currentDate: Date = new Date(); + roleMetadata.createdAt = currentDate.toUTCString(); + roleMetadata.lastModified = currentDate.toUTCString(); + await this.roleMetadataStorage.createRoleMetadata( + roleMetadata, + trx, + ); + } + + const ok = await this.enforcer.addGroupingPolicy(...policy); + if (!ok) { + throw new Error( + `failed to create policy ${policyToString(policy)}`, + ); + } + if (!currentMetadata) { + this.roleEventEmitter.emit('roleAdded', roleMetadata.roleEntityRef); + } + }); if (!externalTrx) { await trx.commit(); } - if (!currentMetadata) { - this.roleEventEmitter.emit('roleAdded', roleMetadata.roleEntityRef); - } } catch (err) { if (!externalTrx) { await trx.rollback(err); @@ -329,51 +348,54 @@ export class EnforcerDelegate implements RoleEventEmitter { oldRoleEntityRef?: string, externalTrx?: Knex.Transaction, ): Promise { - if (this.loadPolicyPromise) { - await this.loadPolicyPromise; - } else { - await this.loadPolicy(); + if (policies.length === 0) { + return; } - const addGroupingPoliciesOperation = (async () => { - if (policies.length === 0) { - return; - } + await this.withCasbinTransaction(externalTrx, () => + this.ensurePolicyLoaded(), + ); + const addGroupingPoliciesOperation = (async () => { const trx = externalTrx ?? (await this.knex.transaction()); try { - const currentRoleMetadata = - await this.roleMetadataStorage.findRoleMetadata( - oldRoleEntityRef ?? roleMetadata.roleEntityRef, - trx, - ); - if (currentRoleMetadata) { - await this.roleMetadataStorage.updateRoleMetadata( - mergeRoleMetadata(currentRoleMetadata, roleMetadata), - oldRoleEntityRef ?? roleMetadata.roleEntityRef, - trx, - ); - } else { - const currentDate: Date = new Date(); - roleMetadata.createdAt = currentDate.toUTCString(); - roleMetadata.lastModified = currentDate.toUTCString(); - await this.roleMetadataStorage.createRoleMetadata(roleMetadata, trx); - } + await this.withCasbinTransaction(trx, async () => { + const currentRoleMetadata = + await this.roleMetadataStorage.findRoleMetadata( + oldRoleEntityRef ?? roleMetadata.roleEntityRef, + trx, + ); + if (currentRoleMetadata) { + await this.roleMetadataStorage.updateRoleMetadata( + mergeRoleMetadata(currentRoleMetadata, roleMetadata), + oldRoleEntityRef ?? roleMetadata.roleEntityRef, + trx, + ); + } else { + const currentDate: Date = new Date(); + roleMetadata.createdAt = currentDate.toUTCString(); + roleMetadata.lastModified = currentDate.toUTCString(); + await this.roleMetadataStorage.createRoleMetadata( + roleMetadata, + trx, + ); + } - const ok = await this.enforcer.addGroupingPolicies(policies); - if (!ok) { - throw new Error( - `Failed to store policies ${policiesToString(policies)}`, - ); - } + const ok = await this.enforcer.addGroupingPolicies(policies); + if (!ok) { + throw new Error( + `Failed to store policies ${policiesToString(policies)}`, + ); + } + if (!currentRoleMetadata) { + this.roleEventEmitter.emit('roleAdded', roleMetadata.roleEntityRef); + } + }); if (!externalTrx) { await trx.commit(); } - if (!currentRoleMetadata) { - this.roleEventEmitter.emit('roleAdded', roleMetadata.roleEntityRef); - } } catch (err) { if (!externalTrx) { await trx.rollback(err); @@ -393,53 +415,55 @@ export class EnforcerDelegate implements RoleEventEmitter { const trx = await this.knex.transaction(); try { - const currentMetadata = await this.roleMetadataStorage.findRoleMetadata( - oldRoleName, - trx, - ); - if (!currentMetadata) { - throw new Error(`Role metadata ${oldRoleName} was not found`); - } - - await this.removeGroupingPolicies(oldRole, currentMetadata, true, trx); - await this.addGroupingPolicies( - newRole, - newRoleMetadata, - currentMetadata.roleEntityRef, - trx, - ); - - // Role name changed -> update roleEntityRef in policies - if (newRoleMetadata.roleEntityRef !== currentMetadata.roleEntityRef) { - const oldPolicies = await this.enforcer.getFilteredPolicy( - 0, - currentMetadata.roleEntityRef, + await this.withCasbinTransaction(trx, async () => { + const currentMetadata = await this.roleMetadataStorage.findRoleMetadata( + oldRoleName, + trx, ); - const updatedPolicies = oldPolicies.map(oldPolicy => [ - newRoleMetadata.roleEntityRef, - ...oldPolicy.slice(1), - ]); - await this.updatePolicies(oldPolicies, updatedPolicies, trx); + if (!currentMetadata) { + throw new Error(`Role metadata ${oldRoleName} was not found`); + } - const oldConditions = await this.conditionalStorage.filterConditions( + await this.removeGroupingPolicies(oldRole, currentMetadata, true, trx); + await this.addGroupingPolicies( + newRole, + newRoleMetadata, currentMetadata.roleEntityRef, - undefined, - undefined, - undefined, - undefined, trx, ); - for (const condition of oldConditions) { - await this.conditionalStorage.updateCondition( - condition.id, - { - ...condition, - roleEntityRef: newRoleMetadata.roleEntityRef, - }, + + // Role name changed -> update roleEntityRef in policies + if (newRoleMetadata.roleEntityRef !== currentMetadata.roleEntityRef) { + const oldPolicies = await this.enforcer.getFilteredPolicy( + 0, + currentMetadata.roleEntityRef, + ); + const updatedPolicies = oldPolicies.map(oldPolicy => [ + newRoleMetadata.roleEntityRef, + ...oldPolicy.slice(1), + ]); + await this.updatePolicies(oldPolicies, updatedPolicies, trx); + + const oldConditions = await this.conditionalStorage.filterConditions( + currentMetadata.roleEntityRef, + undefined, + undefined, + undefined, + undefined, trx, ); + for (const condition of oldConditions) { + await this.conditionalStorage.updateCondition( + condition.id, + { + ...condition, + roleEntityRef: newRoleMetadata.roleEntityRef, + }, + trx, + ); + } } - } + }); await trx.commit(); } catch (err) { @@ -456,8 +480,10 @@ export class EnforcerDelegate implements RoleEventEmitter { const trx = externalTrx ?? (await this.knex.transaction()); try { - await this.removePolicies(oldPolicies, trx); - await this.addPolicies(newPolicies, trx); + await this.withCasbinTransaction(trx, async () => { + await this.removePolicies(oldPolicies, trx); + await this.addPolicies(newPolicies, trx); + }); if (!externalTrx) { await trx.commit(); } @@ -470,29 +496,17 @@ export class EnforcerDelegate implements RoleEventEmitter { } async removePolicy(policy: string[], externalTrx?: Knex.Transaction) { - if (this.loadPolicyPromise) { - await this.loadPolicyPromise; - } else { - await this.loadPolicy(); - } + await this.withCasbinTransaction(externalTrx, () => + this.ensurePolicyLoaded(), + ); const removePolicyOperation = (async () => { - const trx = externalTrx ?? (await this.knex.transaction()); - - try { + await this.withCasbinTransaction(externalTrx, async () => { const ok = await this.enforcer.removePolicy(...policy); if (!ok) { throw new Error(`fail to delete policy ${policy}`); } - if (!externalTrx) { - await trx.commit(); - } - } catch (err) { - if (!externalTrx) { - await trx.rollback(err); - } - throw err; - } + }); })(); await this.execOperation(removePolicyOperation); } @@ -501,32 +515,19 @@ export class EnforcerDelegate implements RoleEventEmitter { policies: string[][], externalTrx?: Knex.Transaction, ): Promise { - if (this.loadPolicyPromise) { - await this.loadPolicyPromise; - } else { - await this.loadPolicy(); - } + await this.withCasbinTransaction(externalTrx, () => + this.ensurePolicyLoaded(), + ); const removePoliciesOperation = (async () => { - const trx = externalTrx ?? (await this.knex.transaction()); - - try { + await this.withCasbinTransaction(externalTrx, async () => { const ok = await this.enforcer.removePolicies(policies); if (!ok) { throw new Error( `Failed to delete policies ${policiesToString(policies)}`, ); } - - if (!externalTrx) { - await trx.commit(); - } - } catch (err) { - if (!externalTrx) { - await trx.rollback(err); - } - throw err; - } + }); })(); await this.execOperation(removePoliciesOperation); } @@ -537,43 +538,48 @@ export class EnforcerDelegate implements RoleEventEmitter { isUpdate?: boolean, externalTrx?: Knex.Transaction, ): Promise { - if (this.loadPolicyPromise) { - await this.loadPolicyPromise; - } else { - await this.loadPolicy(); - } + await this.withCasbinTransaction(externalTrx, () => + this.ensurePolicyLoaded(), + ); const removeGroupingPolicyOperation = (async () => { const trx = externalTrx ?? (await this.knex.transaction()); const roleEntity = policy[1]; try { - const ok = await this.enforcer.removeGroupingPolicy(...policy); - if (!ok) { - throw new Error(`Failed to delete policy ${policyToString(policy)}`); - } + await this.withCasbinTransaction(trx, async () => { + const ok = await this.enforcer.removeGroupingPolicy(...policy); + if (!ok) { + throw new Error( + `Failed to delete policy ${policyToString(policy)}`, + ); + } - if (!isUpdate) { - const currentRoleMetadata = - await this.roleMetadataStorage.findRoleMetadata(roleEntity, trx); - const remainingGroupPolicies = await this.getFilteredGroupingPolicy( - 1, - roleEntity, - ); - if ( - currentRoleMetadata && - remainingGroupPolicies.length === 0 && - roleEntity !== ADMIN_ROLE_NAME - ) { - await this.roleMetadataStorage.removeRoleMetadata(roleEntity, trx); - } else if (currentRoleMetadata) { - await this.roleMetadataStorage.updateRoleMetadata( - mergeRoleMetadata(currentRoleMetadata, roleMetadata), + if (!isUpdate) { + const currentRoleMetadata = + await this.roleMetadataStorage.findRoleMetadata(roleEntity, trx); + const remainingGroupPolicies = await this.getFilteredGroupingPolicy( + 1, roleEntity, - trx, ); + if ( + currentRoleMetadata && + remainingGroupPolicies.length === 0 && + roleEntity !== ADMIN_ROLE_NAME + ) { + await this.roleMetadataStorage.removeRoleMetadata( + roleEntity, + trx, + ); + } else if (currentRoleMetadata) { + await this.roleMetadataStorage.updateRoleMetadata( + mergeRoleMetadata(currentRoleMetadata, roleMetadata), + roleEntity, + trx, + ); + } } - } + }); if (!externalTrx) { await trx.commit(); @@ -594,46 +600,49 @@ export class EnforcerDelegate implements RoleEventEmitter { isUpdate?: boolean, externalTrx?: Knex.Transaction, ): Promise { - if (this.loadPolicyPromise) { - await this.loadPolicyPromise; - } else { - await this.loadPolicy(); - } + await this.withCasbinTransaction(externalTrx, () => + this.ensurePolicyLoaded(), + ); const removeGroupingPolicyOperation = (async () => { const trx = externalTrx ?? (await this.knex.transaction()); const roleEntity = roleMetadata.roleEntityRef; try { - const ok = await this.enforcer.removeGroupingPolicies(policies); - if (!ok) { - throw new Error( - `Failed to delete grouping policies: ${policiesToString(policies)}`, - ); - } - - if (!isUpdate) { - const currentRoleMetadata = - await this.roleMetadataStorage.findRoleMetadata(roleEntity, trx); - const remainingGroupPolicies = await this.getFilteredGroupingPolicy( - 1, - roleEntity, - ); + await this.withCasbinTransaction(trx, async () => { + const ok = await this.enforcer.removeGroupingPolicies(policies); + if (!ok) { + throw new Error( + `Failed to delete grouping policies: ${policiesToString(policies)}`, + ); + } - if ( - currentRoleMetadata && - remainingGroupPolicies.length === 0 && - roleEntity !== ADMIN_ROLE_NAME - ) { - await this.roleMetadataStorage.removeRoleMetadata(roleEntity, trx); - } else if (currentRoleMetadata) { - await this.roleMetadataStorage.updateRoleMetadata( - mergeRoleMetadata(currentRoleMetadata, roleMetadata), + if (!isUpdate) { + const currentRoleMetadata = + await this.roleMetadataStorage.findRoleMetadata(roleEntity, trx); + const remainingGroupPolicies = await this.getFilteredGroupingPolicy( + 1, roleEntity, - trx, ); + + if ( + currentRoleMetadata && + remainingGroupPolicies.length === 0 && + roleEntity !== ADMIN_ROLE_NAME + ) { + await this.roleMetadataStorage.removeRoleMetadata( + roleEntity, + trx, + ); + } else if (currentRoleMetadata) { + await this.roleMetadataStorage.updateRoleMetadata( + mergeRoleMetadata(currentRoleMetadata, roleMetadata), + roleEntity, + trx, + ); + } } - } + }); if (!externalTrx) { await trx.commit(); diff --git a/workspaces/rbac/plugins/rbac-backend/src/service/policy-builder.test.ts b/workspaces/rbac/plugins/rbac-backend/src/service/policy-builder.test.ts index b70345c0e24..4dc7b02e983 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/service/policy-builder.test.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/service/policy-builder.test.ts @@ -17,11 +17,10 @@ import { mockServices } from '@backstage/backend-test-utils'; import type { Adapter, Enforcer } from 'casbin'; import type { Router } from 'express'; -import type TypeORMAdapter from 'typeorm-adapter'; import type { RBACProvider } from '@backstage-community/plugin-rbac-node'; -import { CasbinDBAdapterFactory } from '../database/casbin-adapter-factory'; +import { CasbinKnexAdapter } from '../database/casbin-knex-adapter'; import { RBACPermissionPolicy } from '../policies/permission-policy'; import { PluginPermissionMetadataCollector } from './plugin-endpoints'; import { PoliciesServer } from './policies-rest-api'; @@ -55,17 +54,13 @@ jest.mock('casbin', () => { }; }); -const dataBaseAdapterFactoryMock: Partial = { - createAdapter: jest.fn((): Promise => { - return Promise.resolve({} as TypeORMAdapter); - }), -}; - -jest.mock('../database/casbin-adapter-factory', () => { +jest.mock('../database/casbin-knex-adapter', () => { return { - CasbinDBAdapterFactory: jest.fn((): Partial => { - return dataBaseAdapterFactoryMock; - }), + CasbinKnexAdapter: { + newAdapter: jest.fn((): Promise => { + return Promise.resolve({} as Adapter); + }), + }, }; }); @@ -168,7 +163,7 @@ describe('PolicyBuilder', () => { }, backendPluginIDsProviderMock, ); - expect(CasbinDBAdapterFactory).toHaveBeenCalled(); + expect(CasbinKnexAdapter.newAdapter).toHaveBeenCalled(); expect(enforcerMock.loadPolicy).toHaveBeenCalled(); expect(enforcerMock.enableAutoSave).toHaveBeenCalled(); expect(RBACPermissionPolicy.build).toHaveBeenCalled(); @@ -216,7 +211,7 @@ describe('PolicyBuilder', () => { backendPluginIDsProviderMock, [providerMock], ); - expect(CasbinDBAdapterFactory).toHaveBeenCalled(); + expect(CasbinKnexAdapter.newAdapter).toHaveBeenCalled(); expect(enforcerMock.loadPolicy).toHaveBeenCalled(); expect(enforcerMock.enableAutoSave).toHaveBeenCalled(); expect(RBACPermissionPolicy.build).toHaveBeenCalled(); @@ -261,7 +256,7 @@ describe('PolicyBuilder', () => { }, backendPluginIDsProviderMock, ); - expect(CasbinDBAdapterFactory).toHaveBeenCalled(); + expect(CasbinKnexAdapter.newAdapter).toHaveBeenCalled(); expect(enforcerMock.loadPolicy).toHaveBeenCalled(); expect(enforcerMock.enableAutoSave).toHaveBeenCalled(); expect(RBACPermissionPolicy.build).not.toHaveBeenCalled(); diff --git a/workspaces/rbac/plugins/rbac-backend/src/service/policy-builder.ts b/workspaces/rbac/plugins/rbac-backend/src/service/policy-builder.ts index f85c301dcd7..ffafc392cfb 100644 --- a/workspaces/rbac/plugins/rbac-backend/src/service/policy-builder.ts +++ b/workspaces/rbac/plugins/rbac-backend/src/service/policy-builder.ts @@ -37,7 +37,7 @@ import type { RBACProvider, } from '@backstage-community/plugin-rbac-node'; -import { CasbinDBAdapterFactory } from '../database/casbin-adapter-factory'; +import { CasbinKnexAdapter } from '../database/casbin-knex-adapter'; import { DataBaseConditionalStorage } from '../database/conditional-storage'; import { migrate } from '../database/migration'; import { DataBaseRoleMetadataStorage } from '../database/role-metadata'; @@ -110,10 +110,9 @@ export class PolicyBuilder { const databaseClient = await databaseManager.getClient(); - const adapter = await new CasbinDBAdapterFactory( - env.config, - databaseClient, - ).createAdapter(); + await migrate(databaseManager, databaseClient); + + const adapter = await CasbinKnexAdapter.newAdapter(databaseClient); const enf = await newEnforcer(newModelFromString(MODEL), adapter); await enf.loadPolicy(); @@ -139,8 +138,6 @@ export class PolicyBuilder { enf.enableAutoBuildRoleLinks(false); await enf.buildRoleLinks(); - await migrate(databaseManager); - const conditionStorage = new DataBaseConditionalStorage(databaseClient); const roleMetadataStorage = new DataBaseRoleMetadataStorage(databaseClient); diff --git a/workspaces/rbac/yarn.lock b/workspaces/rbac/yarn.lock index f38c830ae4e..1b171d90d6c 100644 --- a/workspaces/rbac/yarn.lock +++ b/workspaces/rbac/yarn.lock @@ -1532,13 +1532,13 @@ __metadata: linkType: hard "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.15, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.26.2, @babel/parser@npm:^7.28.6, @babel/parser@npm:^7.29.0": - version: 7.29.7 - resolution: "@babel/parser@npm:7.29.7" + version: 7.29.8 + resolution: "@babel/parser@npm:7.29.8" dependencies: - "@babel/types": "npm:^7.29.7" + "@babel/types": "npm:^7.29.8" bin: parser: ./bin/babel-parser.js - checksum: 10/da40c5928c95997b01aabe84fc3440881b8f20b866714fefa142961d37e82ffc03fbb9afed706f15f8a688278f95286ca0cea0d87ad6c77600f8c6c45d9824ee + checksum: 10/dbd14ebbba0fa3019acd2712b0db3ffd594d0850d4fc487667287b5702893f54a7911570ea842f0cff4dc492a2412e3287dfabfe00c6b78efa7becb1255f3f86 languageName: node linkType: hard @@ -1762,13 +1762,13 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.26.0, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.6, @babel/types@npm:^7.29.0, @babel/types@npm:^7.29.7": - version: 7.29.7 - resolution: "@babel/types@npm:7.29.7" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.26.0, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.6, @babel/types@npm:^7.29.0, @babel/types@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/types@npm:7.29.8" dependencies: "@babel/helper-string-parser": "npm:^7.29.7" "@babel/helper-validator-identifier": "npm:^7.29.7" - checksum: 10/bd4f5635db1057bd0abeebf93eb3ae38399e152271cea8dce8288350f0afa13ed3e2db2e16e22bd3303068890eec18965a83420539afbe0dde31432b4cf9636d + checksum: 10/ff2becf0f8b432f0820f286fb9319d7f3819b2d0eb95bc26957fbc6250a3ca0ae3656feb98ecb5f171f9e04b34a4c08f5036447f17459ed7bfc32ff649d9b71e languageName: node linkType: hard @@ -1814,7 +1814,6 @@ __metadata: lodash: "npm:^4.17.21" qs: "npm:6.15.3" supertest: "npm:7.2.2" - typeorm-adapter: "npm:^1.6.1" zod: "npm:^4.3.6" languageName: unknown linkType: soft @@ -1897,15 +1896,15 @@ __metadata: languageName: unknown linkType: soft -"@backstage/app-defaults@npm:^1.7.9": - version: 1.7.9 - resolution: "@backstage/app-defaults@npm:1.7.9" +"@backstage/app-defaults@npm:^1.7.10": + version: 1.7.10 + resolution: "@backstage/app-defaults@npm:1.7.10" dependencies: - "@backstage/core-app-api": "npm:^1.20.2" - "@backstage/core-components": "npm:^0.18.11" - "@backstage/core-plugin-api": "npm:^1.12.7" - "@backstage/frontend-plugin-api": "npm:^0.17.2" - "@backstage/plugin-permission-react": "npm:^0.5.2" + "@backstage/core-app-api": "npm:^1.20.3" + "@backstage/core-components": "npm:^0.18.12" + "@backstage/core-plugin-api": "npm:^1.12.8" + "@backstage/frontend-plugin-api": "npm:^0.17.3" + "@backstage/plugin-permission-react": "npm:^0.5.3" "@backstage/theme": "npm:^0.7.3" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" @@ -1917,25 +1916,25 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/e9af70e72ecd2b09c34311e5ee19972f0460baf512cfcd2e0452b9b8372728df3aa9a9716f079a629ae173df2ab92329f5d5e2d9b3a27f2aebe710a06719b3b3 + checksum: 10/398062183b83521c90edfbc9fd58320548c444e6db85f79e564c65e42d4801032466d6b87ba093d0c1a94b620715fc504436aa89a0c7a50bb14b73ac3bceb4c9 languageName: node linkType: hard -"@backstage/backend-app-api@npm:^1.7.1": - version: 1.7.1 - resolution: "@backstage/backend-app-api@npm:1.7.1" +"@backstage/backend-app-api@npm:^1.7.2": + version: 1.7.2 + resolution: "@backstage/backend-app-api@npm:1.7.2" dependencies: - "@backstage/backend-plugin-api": "npm:^1.9.2" + "@backstage/backend-plugin-api": "npm:^1.9.3" "@backstage/config": "npm:^1.3.8" - "@backstage/connections": "npm:^0.1.0" + "@backstage/connections": "npm:^0.2.0" "@backstage/errors": "npm:^1.3.1" - checksum: 10/728d502f079862236956a8c982feddc4f9c87ef1edd9cb527b1c4e1c10595c590db2b2b08ff538476087a65e1e4bec9988399f3102a9e27ce970374e5b76f366 + checksum: 10/9175a60077fa3f8206c1063d805f6a625b53f2035d1da4d330ccb178b9c720c293b65338a4d2348a5ecf144cc99ce266688bcc161b1d1a35020ad733b7f2ed95 languageName: node linkType: hard -"@backstage/backend-defaults@npm:^0.17.3": - version: 0.17.3 - resolution: "@backstage/backend-defaults@npm:0.17.3" +"@backstage/backend-defaults@npm:^0.17.3, @backstage/backend-defaults@npm:^0.17.5": + version: 0.17.6 + resolution: "@backstage/backend-defaults@npm:0.17.6" dependencies: "@aws-sdk/abort-controller": "npm:^3.347.0" "@aws-sdk/client-codecommit": "npm:^3.350.0" @@ -1945,19 +1944,19 @@ __metadata: "@aws-sdk/types": "npm:^3.347.0" "@azure/identity": "npm:^4.0.0" "@azure/storage-blob": "npm:^12.5.0" - "@backstage/backend-app-api": "npm:^1.7.1" + "@backstage/backend-app-api": "npm:^1.7.2" "@backstage/backend-dev-utils": "npm:^0.1.7" - "@backstage/backend-plugin-api": "npm:^1.9.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/backend-plugin-api": "npm:^1.9.3" + "@backstage/cli-node": "npm:^0.3.4" "@backstage/config": "npm:^1.3.8" - "@backstage/config-loader": "npm:^1.10.12" + "@backstage/config-loader": "npm:^1.11.1" "@backstage/errors": "npm:^1.3.1" "@backstage/integration": "npm:^2.0.3" "@backstage/integration-aws-node": "npm:^0.2.0" - "@backstage/plugin-auth-node": "npm:^0.7.2" - "@backstage/plugin-events-node": "npm:^0.4.23" + "@backstage/plugin-auth-node": "npm:^0.7.3" + "@backstage/plugin-events-node": "npm:^0.4.24" "@backstage/plugin-permission-common": "npm:^0.9.9" - "@backstage/plugin-permission-node": "npm:^0.11.1" + "@backstage/plugin-permission-node": "npm:^0.11.2" "@backstage/types": "npm:^1.2.2" "@google-cloud/storage": "npm:^7.0.0" "@keyv/memcache": "npm:^2.0.1" @@ -2018,7 +2017,7 @@ __metadata: optional: true better-sqlite3: optional: true - checksum: 10/d583d9fc37094eca00ec8e8bd71a8e596b3a4cd78ba3ea0ebafa9001217d3742288b57fe3d3ac4954edad58abf30eff475e82b6372c4e75b42582498c44d2996 + checksum: 10/b785ffe4ea814a8f8fb7fab73fa6d4bf4c8e688b3caa929a060b607d6d1ab8d389bf03138d08fb7f2687f9fcc5d32b657ec80399750c2b7b2fb913e77843a5c0 languageName: node linkType: hard @@ -2029,12 +2028,12 @@ __metadata: languageName: node linkType: hard -"@backstage/backend-openapi-utils@npm:^0.6.10": - version: 0.6.10 - resolution: "@backstage/backend-openapi-utils@npm:0.6.10" +"@backstage/backend-openapi-utils@npm:^0.7.0": + version: 0.7.0 + resolution: "@backstage/backend-openapi-utils@npm:0.7.0" dependencies: "@apidevtools/swagger-parser": "npm:^10.1.0" - "@backstage/backend-plugin-api": "npm:^1.9.2" + "@backstage/backend-plugin-api": "npm:^1.9.3" "@backstage/errors": "npm:^1.3.1" "@backstage/types": "npm:^1.2.2" "@types/express": "npm:^4.17.6" @@ -2049,7 +2048,7 @@ __metadata: mockttp: "npm:^3.13.0" openapi-merge: "npm:^1.3.2" openapi3-ts: "npm:^3.1.2" - checksum: 10/56ba10cee438924b221f2219794d917377705acc16d6547083cec5836e50a11d5b288ccbfd20383be4b22815a9b767523c6212dfc99a2302bb24a92127629570 + checksum: 10/4adc8b3b0d068d7d624d7dd5b1e2a38a850d410dfdeb105a3fdc62811ade22cf4d8a6759c13ae552e05ab8315d9767d98e7b806910c9ae62194db4d94e744ebf languageName: node linkType: hard @@ -2075,16 +2074,16 @@ __metadata: linkType: hard "@backstage/backend-test-utils@npm:^1.11.4": - version: 1.11.4 - resolution: "@backstage/backend-test-utils@npm:1.11.4" + version: 1.11.5 + resolution: "@backstage/backend-test-utils@npm:1.11.5" dependencies: - "@backstage/backend-app-api": "npm:^1.7.1" - "@backstage/backend-defaults": "npm:^0.17.3" - "@backstage/backend-plugin-api": "npm:^1.9.2" + "@backstage/backend-app-api": "npm:^1.7.2" + "@backstage/backend-defaults": "npm:^0.17.5" + "@backstage/backend-plugin-api": "npm:^1.9.3" "@backstage/config": "npm:^1.3.8" "@backstage/errors": "npm:^1.3.1" - "@backstage/plugin-auth-node": "npm:^0.7.2" - "@backstage/plugin-events-node": "npm:^0.4.23" + "@backstage/plugin-auth-node": "npm:^0.7.3" + "@backstage/plugin-events-node": "npm:^0.4.24" "@backstage/plugin-permission-common": "npm:^0.9.9" "@backstage/types": "npm:^1.2.2" "@keyv/memcache": "npm:^2.0.1" @@ -2114,7 +2113,7 @@ __metadata: peerDependenciesMeta: "@types/jest": optional: true - checksum: 10/209b0bf13e49d5b84a60ca36095cd4157a638b278f2ccb36dee8fddb4aa5466f77f664b0a91e5dacf9681d8eb5dcaeb61110d3009c7a8182c40c34677dd50d36 + checksum: 10/5b3f3e772deba200acf41e1fd2d26c6dd4b63c9dee4bf5b9126e4946d1c16bc74b076eb63ce0156f0677d3ffe79b16810455832919b28ce5d801863d41136707 languageName: node linkType: hard @@ -2169,31 +2168,31 @@ __metadata: languageName: node linkType: hard -"@backstage/cli-defaults@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-defaults@npm:0.1.3" +"@backstage/cli-defaults@npm:^0.1.3, @backstage/cli-defaults@npm:^0.1.4": + version: 0.1.4 + resolution: "@backstage/cli-defaults@npm:0.1.4" dependencies: - "@backstage/cli-module-actions": "npm:^0.1.2" - "@backstage/cli-module-auth": "npm:^0.1.3" - "@backstage/cli-module-build": "npm:^0.1.4" - "@backstage/cli-module-config": "npm:^0.1.3" - "@backstage/cli-module-github": "npm:^0.1.3" - "@backstage/cli-module-info": "npm:^0.1.3" - "@backstage/cli-module-lint": "npm:^0.1.3" - "@backstage/cli-module-maintenance": "npm:^0.1.3" - "@backstage/cli-module-migrate": "npm:^0.1.3" - "@backstage/cli-module-new": "npm:^0.1.4" - "@backstage/cli-module-test-jest": "npm:^0.1.3" - "@backstage/cli-module-translations": "npm:^0.1.3" - checksum: 10/5809f370defc1729e177e8e10728a00974cf7e1bebb897bb81a08aedea682fb48edbdf82825c2ae74843c70566abecd087e6a92f69319657033684700a5f8c32 + "@backstage/cli-module-actions": "npm:^0.1.3" + "@backstage/cli-module-auth": "npm:^0.1.4" + "@backstage/cli-module-build": "npm:^0.1.5" + "@backstage/cli-module-config": "npm:^0.1.4" + "@backstage/cli-module-github": "npm:^0.1.4" + "@backstage/cli-module-info": "npm:^0.1.4" + "@backstage/cli-module-lint": "npm:^0.1.4" + "@backstage/cli-module-maintenance": "npm:^0.1.4" + "@backstage/cli-module-migrate": "npm:^0.2.0" + "@backstage/cli-module-new": "npm:^0.1.5" + "@backstage/cli-module-test-jest": "npm:^0.1.4" + "@backstage/cli-module-translations": "npm:^0.1.4" + checksum: 10/d74d4a2368ee325519aaa2294859502bda4351421d46f231fb6e22daa1f1da4c28b33314555b7e2959374aa15d9b666981ad947ebb35fc2de0860c60b7d520a6 languageName: node linkType: hard -"@backstage/cli-module-actions@npm:^0.1.2": - version: 0.1.2 - resolution: "@backstage/cli-module-actions@npm:0.1.2" +"@backstage/cli-module-actions@npm:^0.1.3": + version: 0.1.3 + resolution: "@backstage/cli-module-actions@npm:0.1.3" dependencies: - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-node": "npm:^0.3.4" "@backstage/errors": "npm:^1.3.1" chalk: "npm:^4.0.0" cleye: "npm:^2.6.0" @@ -2203,15 +2202,15 @@ __metadata: zod: "npm:^3.25.76 || ^4.0.0" bin: cli-module-actions: bin/backstage-cli-module-actions - checksum: 10/c3b5a15917651e03fcf6e74121aec6e3340625e01e696f122e7823ffb6839031e851c8fec267f0fb41a56499783ff02a901fd20fa971a18777d13b541a8f717e + checksum: 10/3263ee8cd948ff01034de66ebf69666f0ab58144bd1079bdddd8b6943b1b0df83b9db0a9ae644dffb48a49561bce644ae1cdc9a607ed6cf5874e231ca46a9bc5 languageName: node linkType: hard -"@backstage/cli-module-auth@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-module-auth@npm:0.1.3" +"@backstage/cli-module-auth@npm:^0.1.4": + version: 0.1.4 + resolution: "@backstage/cli-module-auth@npm:0.1.4" dependencies: - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-node": "npm:^0.3.4" "@backstage/errors": "npm:^1.3.1" cleye: "npm:^2.6.0" fs-extra: "npm:^11.2.0" @@ -2226,18 +2225,18 @@ __metadata: optional: true bin: cli-module-auth: bin/backstage-cli-module-auth - checksum: 10/a925dbc3a0e63740d8be78a201d8a04917c112963f3042076837af366cbb612c14bd1a5809f3a9ff8e4b3fa682731587a835f564a8370e5f2182026339bd9807 + checksum: 10/1ddd16687ba1602579ee0bd7c233c656849364a9917ccdd527b725a0a69e96e87154c2711b19263f12d1268e1cf2dff0f2a3586ff904d6ab525a4d7dff76689f languageName: node linkType: hard -"@backstage/cli-module-build@npm:^0.1.4": - version: 0.1.4 - resolution: "@backstage/cli-module-build@npm:0.1.4" +"@backstage/cli-module-build@npm:^0.1.5": + version: 0.1.6 + resolution: "@backstage/cli-module-build@npm:0.1.6" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" "@backstage/config": "npm:^1.3.8" - "@backstage/config-loader": "npm:^1.10.12" + "@backstage/config-loader": "npm:^1.11.1" "@backstage/errors": "npm:^1.3.1" "@backstage/module-federation-common": "npm:^0.1.4" "@manypkg/get-packages": "npm:^1.1.3" @@ -2294,6 +2293,7 @@ __metadata: webpack-dev-server: "npm:^5.0.0" yml-loader: "npm:^2.1.0" yn: "npm:^4.0.0" + zod: "npm:^3.25.76 || ^4.0.0" peerDependencies: embedded-postgres: ^18.3.0-beta.16 peerDependenciesMeta: @@ -2301,18 +2301,18 @@ __metadata: optional: true bin: cli-module-build: bin/backstage-cli-module-build - checksum: 10/27b924c35032bd8c5346f3a021abc41af55a7c2e5d2dfa2671989c3acc71230938b9e39ba32e21835396c7b6e9ac14f633a29f584ae7127728222bbadb77ac59 + checksum: 10/6837a1927bcec14c46f1beec6cf0f32e402fc96ff0913c77c449cac3bba768907b56a1bb39a447043d4745b8e80a499cf8b2a103b84748cf4f795e29503d2b25 languageName: node linkType: hard -"@backstage/cli-module-config@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-module-config@npm:0.1.3" +"@backstage/cli-module-config@npm:^0.1.4": + version: 0.1.5 + resolution: "@backstage/cli-module-config@npm:0.1.5" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" "@backstage/config": "npm:^1.3.8" - "@backstage/config-loader": "npm:^1.10.12" + "@backstage/config-loader": "npm:^1.11.1" "@backstage/types": "npm:^1.2.2" "@manypkg/get-packages": "npm:^1.1.3" chalk: "npm:^4.0.0" @@ -2321,16 +2321,16 @@ __metadata: yaml: "npm:^2.0.0" bin: cli-module-config: bin/backstage-cli-module-config - checksum: 10/fef1e908a6cb3d386b4cf501e6581bcf1272502e9784c474d3b2c34d705437f8bbe19a319ed91d9675af8c34ddf515f5f5975854bee9eaf987f387a394659957 + checksum: 10/3cfa9f93de0072df861242284857a96634fcf8935e738ceb6fb52225e60e722ed2bf4ca6b9b4a675ed2851080b8de75eb734b724ce63b1c9e89bebd855aa4413 languageName: node linkType: hard -"@backstage/cli-module-github@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-module-github@npm:0.1.3" +"@backstage/cli-module-github@npm:^0.1.4": + version: 0.1.4 + resolution: "@backstage/cli-module-github@npm:0.1.4" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" "@octokit/request": "npm:^8.0.0" chalk: "npm:^4.0.0" cleye: "npm:^2.6.0" @@ -2341,31 +2341,31 @@ __metadata: yaml: "npm:^2.0.0" bin: cli-module-github: bin/backstage-cli-module-github - checksum: 10/684a6bd4223df788b5025b4fd6ca0e4e8ee26649e14b90495764d2668cd711de37a40dc138b6cdb92ee3bedcb95fcf582e96082090a5ecbce8a8d34b996b160b + checksum: 10/95e9183426569bf6e37ac2a1bfc2057204a71c5174097b7ab5700668eaeef74d910fd3fe9324ff8b9878ccafc51facfcb90e6c3708795fd0bea791da63a068a2 languageName: node linkType: hard -"@backstage/cli-module-info@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-module-info@npm:0.1.3" +"@backstage/cli-module-info@npm:^0.1.4": + version: 0.1.4 + resolution: "@backstage/cli-module-info@npm:0.1.4" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" cleye: "npm:^2.6.0" fs-extra: "npm:^11.2.0" minimatch: "npm:^10.2.1" bin: cli-module-info: bin/backstage-cli-module-info - checksum: 10/5824e77b0f80c4e1af628442953d21b9e2bd602e0b663a6af43df5af2f8604a46abe6890c1afe70e3f3d55bffff36cf8cbc3329d2fe98ada30483bdbb4435871 + checksum: 10/1566ab9307ef4b318d680e4aa29e9cd327f0a7f93231147371176734337b7f0e42481a65b4537f8978cb722775f92e057fb51c5ee4f8ba13a9701dcef5960fba languageName: node linkType: hard -"@backstage/cli-module-lint@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-module-lint@npm:0.1.3" +"@backstage/cli-module-lint@npm:^0.1.4": + version: 0.1.4 + resolution: "@backstage/cli-module-lint@npm:0.1.4" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" chalk: "npm:^4.0.0" cleye: "npm:^2.6.0" eslint: "npm:^8.6.0" @@ -2375,32 +2375,32 @@ __metadata: shell-quote: "npm:^1.8.1" bin: cli-module-lint: bin/backstage-cli-module-lint - checksum: 10/1c3cd7d5c5484759ad500a11647f6cb4b239d231fc54a499e82041efe0c3bab37c43c6c4889adea56a4bc55dbb1ece7622ff976c99b16833726277c0ba62fff6 + checksum: 10/5354b2e8a9e85a595deaf69cddf69670313204cc47e40c783cf44fe7b9a736b6fe9a0ad72c6bf25451831c5f7ed80bb64647239ef6fd466b6bca853ea4b1349f languageName: node linkType: hard -"@backstage/cli-module-maintenance@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-module-maintenance@npm:0.1.3" +"@backstage/cli-module-maintenance@npm:^0.1.4": + version: 0.1.4 + resolution: "@backstage/cli-module-maintenance@npm:0.1.4" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" chalk: "npm:^4.0.0" cleye: "npm:^2.6.0" eslint: "npm:^8.6.0" fs-extra: "npm:^11.2.0" bin: cli-module-maintenance: bin/backstage-cli-module-maintenance - checksum: 10/b992e8645cb3548c875c1ed82481d4e3e41efd8c51dbfc545450f61cc30d0fb1baee804b73c16a5204c3d3871206a6a3e270dca633531b7c7441a0fe0e4492af + checksum: 10/5e5b66396b187b15907cbcc51aa25308f02d40b624cdf4e6a2cd290f8ad4e5a2083cc2caf452ae1e8db37015952c3e95bb0a8b985a8f085451edce0df1161d4f languageName: node linkType: hard -"@backstage/cli-module-migrate@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-module-migrate@npm:0.1.3" +"@backstage/cli-module-migrate@npm:^0.2.0": + version: 0.2.0 + resolution: "@backstage/cli-module-migrate@npm:0.2.0" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" "@backstage/errors": "npm:^1.3.1" "@backstage/release-manifests": "npm:^0.0.13" "@manypkg/get-packages": "npm:^1.1.3" @@ -2413,16 +2413,16 @@ __metadata: semver: "npm:^7.5.3" bin: cli-module-migrate: bin/backstage-cli-module-migrate - checksum: 10/0453839233e982bbb0a9578c6fb391676a09a3ef6fce0a5c43c410f954de49f1e800b7f0b8586586fde0ec02cb83a86417fe57ae4796344f54f90e315a419b99 + checksum: 10/5decef706398d2e6a964a09b564badc6b3bcf2916e5632fb885391a369198075d06f8fc6418f85b5ff1370767bec0c99b703ab96a59aff72340fe628e1aee117 languageName: node linkType: hard -"@backstage/cli-module-new@npm:^0.1.4": - version: 0.1.4 - resolution: "@backstage/cli-module-new@npm:0.1.4" +"@backstage/cli-module-new@npm:^0.1.5": + version: 0.1.5 + resolution: "@backstage/cli-module-new@npm:0.1.5" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" "@backstage/errors": "npm:^1.3.1" chalk: "npm:^4.0.0" cleye: "npm:^2.6.0" @@ -2435,19 +2435,19 @@ __metadata: semver: "npm:^7.5.3" yaml: "npm:^2.0.0" zod: "npm:^3.25.76" - zod-validation-error: "npm:^4.0.2" + zod-validation-error: "npm:^5.0.0" bin: cli-module-new: bin/backstage-cli-module-new - checksum: 10/2a28cf43f7303167ce4ef21f9d00cbabc5c1aa31f19783359b9acc520ac3ebf53c478745485982688267e2ca2ee215ec12c34649d1918f60213159afb31bb2db + checksum: 10/72ad4e357e4fd340ce6fc118675ffc77a8c6bb87d20bd9c4ee594a90b8861b1ea90241f4659024b345406043ddec76cfa0c14062a358f6563950674badf0320d languageName: node linkType: hard -"@backstage/cli-module-test-jest@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-module-test-jest@npm:0.1.3" +"@backstage/cli-module-test-jest@npm:^0.1.4": + version: 0.1.4 + resolution: "@backstage/cli-module-test-jest@npm:0.1.4" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" "@swc/core": "npm:^1.15.6" "@swc/jest": "npm:^0.2.39" cleye: "npm:^2.6.0" @@ -2471,30 +2471,30 @@ __metadata: optional: true bin: cli-module-test-jest: bin/backstage-cli-module-test-jest - checksum: 10/742337ad15caa3e3b6c3f8e3dbb5415657b62ac863faf72b01665b75a40a87e547f70fe70368bbada75d47675e359a8bb0976afd5ec01ce1f86d1185d4f99eea + checksum: 10/88f87463e3c2b8a78028ea5b11d8ff228aea1a1189db495b88b866a6b647e7ccb42c8ac0ad1e3e7fab72cf5866264b6333d2300b100dc499eac84563560b7ec0 languageName: node linkType: hard -"@backstage/cli-module-translations@npm:^0.1.3": - version: 0.1.3 - resolution: "@backstage/cli-module-translations@npm:0.1.3" +"@backstage/cli-module-translations@npm:^0.1.4": + version: 0.1.4 + resolution: "@backstage/cli-module-translations@npm:0.1.4" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-node": "npm:^0.3.4" cleye: "npm:^2.6.0" fs-extra: "npm:^11.2.0" ts-morph: "npm:^24.0.0" bin: cli-module-translations: bin/backstage-cli-module-translations - checksum: 10/fe2800655bd4483d2828a8350ea7ef0c6486357b535f799f03cd585f4ed3dd3208f2e194bd24a7cb77017fcbc9fc14df34c127eefd3104a07b9979886fa9dd7e + checksum: 10/bf43373095c0191d35786571e655e53bdfb79f3068cfbf50eb3f5ed43f705b2d03bcc2feae410d50fca18991804cb3abd0bf59da1de1a32443edac79785c122a languageName: node linkType: hard -"@backstage/cli-node@npm:^0.3.3": - version: 0.3.3 - resolution: "@backstage/cli-node@npm:0.3.3" +"@backstage/cli-node@npm:^0.3.3, @backstage/cli-node@npm:^0.3.4": + version: 0.3.4 + resolution: "@backstage/cli-node@npm:0.3.4" dependencies: - "@backstage/cli-common": "npm:^0.2.2" + "@backstage/cli-common": "npm:^0.3.0" "@backstage/errors": "npm:^1.3.1" "@backstage/types": "npm:^1.2.2" "@manypkg/get-packages": "npm:^1.1.3" @@ -2517,19 +2517,19 @@ __metadata: peerDependenciesMeta: "@swc/core": optional: true - checksum: 10/0d14768b309af12a6d29a8048a110dd4f2993aeca35115199f9e12aae9113c7088a60575018ad76f5fad102f984ac9f93edd775b7b9213aad430b7001ae237d3 + checksum: 10/ad8806510910569831e107ad512b0866cdad137c9966c5bd27e2fdbfd46a79ef97052882ff59d77d20529f6d7ee4c14310d978cbc404e81ccb61abcfda80ab75 languageName: node linkType: hard "@backstage/cli@npm:^0.36.3": - version: 0.36.3 - resolution: "@backstage/cli@npm:0.36.3" + version: 0.36.4 + resolution: "@backstage/cli@npm:0.36.4" dependencies: - "@backstage/cli-common": "npm:^0.2.2" - "@backstage/cli-defaults": "npm:^0.1.3" - "@backstage/cli-module-build": "npm:^0.1.4" - "@backstage/cli-module-test-jest": "npm:^0.1.3" - "@backstage/cli-node": "npm:^0.3.3" + "@backstage/cli-common": "npm:^0.3.0" + "@backstage/cli-defaults": "npm:^0.1.4" + "@backstage/cli-module-build": "npm:^0.1.5" + "@backstage/cli-module-test-jest": "npm:^0.1.4" + "@backstage/cli-node": "npm:^0.3.4" "@backstage/eslint-plugin": "npm:^0.3.1" "@manypkg/get-packages": "npm:^1.1.3" "@spotify/eslint-config-base": "npm:^15.0.0" @@ -2572,15 +2572,15 @@ __metadata: optional: true bin: backstage-cli: bin/backstage-cli - checksum: 10/ddce05ec9898a80b7fd575cccf360843477ce00d30a2924c79c7755b1ccb57cf3fb01788401194d12b59ca2ab2c6d5b0494d9178c89fecdd584b7095d7f21465 + checksum: 10/07799b56c3f050ed7ec62345437c1bdb0fbd6fedc84d11d5cf937cd0cf8d23c3cd200781ca018cf0d76f30212043c95b45cee60a530e762eb5efd3d14c3febaa languageName: node linkType: hard -"@backstage/config-loader@npm:^1.10.12": - version: 1.10.12 - resolution: "@backstage/config-loader@npm:1.10.12" +"@backstage/config-loader@npm:^1.10.12, @backstage/config-loader@npm:^1.11.1": + version: 1.11.1 + resolution: "@backstage/config-loader@npm:1.11.1" dependencies: - "@backstage/cli-common": "npm:^0.2.2" + "@backstage/cli-common": "npm:^0.3.0" "@backstage/config": "npm:^1.3.8" "@backstage/errors": "npm:^1.3.1" "@backstage/types": "npm:^1.2.2" @@ -2592,9 +2592,10 @@ __metadata: json-schema-traverse: "npm:^1.0.0" lodash: "npm:^4.17.21" minimist: "npm:^1.2.5" - typescript-json-schema: "npm:^0.67.0" + ts-json-schema-generator: "npm:^2.9.0" + typescript: "npm:^5.9.3" yaml: "npm:^2.0.0" - checksum: 10/aacb8f3c28cec8bcb715604a582db43df326924aa8a8718c9bd13789a6fa8514c5dd24db6d39d62331bd17321c63210cb4650bfd712c0d73a608b114ec9ac438 + checksum: 10/e7945d78bbd47474f5acfc3678951dc8e93f8e3ee9bf00c5f3495f34585b929e940f169cc7f9c990af4304d927941d0166cd68ae531eeca30a169c6f752a3542 languageName: node linkType: hard @@ -2609,27 +2610,27 @@ __metadata: languageName: node linkType: hard -"@backstage/connections@npm:^0.1.0": - version: 0.1.0 - resolution: "@backstage/connections@npm:0.1.0" +"@backstage/connections@npm:^0.2.0": + version: 0.2.0 + resolution: "@backstage/connections@npm:0.2.0" dependencies: - "@backstage/backend-plugin-api": "npm:^1.9.2" + "@backstage/backend-plugin-api": "npm:^1.9.3" "@backstage/config": "npm:^1.3.8" "@backstage/errors": "npm:^1.3.1" "@backstage/types": "npm:^1.2.2" zod: "npm:^3.25.76 || ^4.0.0" - checksum: 10/67faac5c4e826be38f20ce5787fa551ce2d08d61e775a39bb3a16b520f287f01e3b39e40808ce090684a17ab405ad3191b69f203494d0cd0b00f1e1359d2c504 + checksum: 10/b91c88274ab5e090a8bb09c3c19b692c87dc60003e40e35474be188383349bac582bec4fc1fe8d4d32eaa93059d661c4276ec21bec78ecc14ed725eda21ec7f5 languageName: node linkType: hard -"@backstage/core-app-api@npm:^1.20.2": - version: 1.20.2 - resolution: "@backstage/core-app-api@npm:1.20.2" +"@backstage/core-app-api@npm:^1.20.2, @backstage/core-app-api@npm:^1.20.3": + version: 1.20.3 + resolution: "@backstage/core-app-api@npm:1.20.3" dependencies: "@backstage/config": "npm:^1.3.8" - "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/core-plugin-api": "npm:^1.12.8" "@backstage/types": "npm:^1.2.2" - "@backstage/ui": "npm:^0.16.0" + "@backstage/ui": "npm:^0.17.0" "@backstage/version-bridge": "npm:^1.0.12" "@types/prop-types": "npm:^15.7.3" history: "npm:^5.0.0" @@ -2647,20 +2648,20 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/e1e3a4dcf5f4f19e1d94a540b3e85ebb1cb211154aa2eeb8dd7acf9fcddf16864cf268ea2d5b51cbf7c77310f9e748446787e48742a331d824405cab8734c4bb + checksum: 10/cff273c71591919b20b5bceab05310da3d10ce1717cb623eb40d5d15892692dd56dd6e26d2287fd794d38c87cafac872451399627139c9b69edef897d1286dd3 languageName: node linkType: hard -"@backstage/core-compat-api@npm:^0.5.12": - version: 0.5.12 - resolution: "@backstage/core-compat-api@npm:0.5.12" +"@backstage/core-compat-api@npm:^0.5.13": + version: 0.5.13 + resolution: "@backstage/core-compat-api@npm:0.5.13" dependencies: - "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/core-plugin-api": "npm:^1.12.8" "@backstage/errors": "npm:^1.3.1" - "@backstage/filter-predicates": "npm:^0.1.3" - "@backstage/frontend-plugin-api": "npm:^0.17.2" - "@backstage/plugin-app-react": "npm:^0.2.4" - "@backstage/plugin-catalog-react": "npm:^3.1.0" + "@backstage/filter-predicates": "npm:^0.1.4" + "@backstage/frontend-plugin-api": "npm:^0.17.3" + "@backstage/plugin-app-react": "npm:^0.2.5" + "@backstage/plugin-catalog-react": "npm:^3.2.0" "@backstage/types": "npm:^1.2.2" "@backstage/version-bridge": "npm:^1.0.12" lodash: "npm:^4.17.21" @@ -2672,18 +2673,19 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/ddc78907e803a50cd9f0dff8cced126e0c8d0e071148937efb554af5fcd449b4acc6015150ed900a7c2c4ff0fa071b2cc4937571f479dd58797b5a9fd4615e18 + checksum: 10/94357b529a62b412b445f1be5bd28668e86e68e8a2db7aa649958aa77ea72c5a9d24ba2e2fcd5e1b67a4c59a4553eb9c33a5b502b95a79f880267708caf8506e languageName: node linkType: hard -"@backstage/core-components@npm:^0.18.11": - version: 0.18.11 - resolution: "@backstage/core-components@npm:0.18.11" +"@backstage/core-components@npm:^0.18.11, @backstage/core-components@npm:^0.18.12": + version: 0.18.12 + resolution: "@backstage/core-components@npm:0.18.12" dependencies: "@backstage/config": "npm:^1.3.8" - "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/core-plugin-api": "npm:^1.12.8" "@backstage/errors": "npm:^1.3.1" "@backstage/theme": "npm:^0.7.3" + "@backstage/ui": "npm:^0.17.0" "@backstage/version-bridge": "npm:^1.0.12" "@dagrejs/dagre": "npm:^1.1.4" "@date-io/core": "npm:^1.3.13" @@ -2700,7 +2702,7 @@ __metadata: d3-selection: "npm:^3.0.0" d3-shape: "npm:^3.0.0" d3-zoom: "npm:^3.0.0" - js-yaml: "npm:^4.1.0" + js-yaml: "npm:^4.2.0" linkify-react: "npm:4.3.2" linkifyjs: "npm:4.3.2" lodash: "npm:^4.17.21" @@ -2730,17 +2732,17 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/820bfd765ee4effeb7cb6e54adba85acce98f59e60affd85f7ee72c78033d47643efa086f6dca0ae110e9430fbb2c19132442a0742f9c7985db870812bac3a60 + checksum: 10/501f554154e9fd3f99656cca43be7e779bc2cc17ca970015f5fa7d546e31bc584d535789203cb270a6ca6d8683af3f7606a96e2bdcf53f76ca6278d7bbfcb08d languageName: node linkType: hard -"@backstage/core-plugin-api@npm:^1.12.7": - version: 1.12.7 - resolution: "@backstage/core-plugin-api@npm:1.12.7" +"@backstage/core-plugin-api@npm:^1.12.7, @backstage/core-plugin-api@npm:^1.12.8": + version: 1.12.8 + resolution: "@backstage/core-plugin-api@npm:1.12.8" dependencies: "@backstage/config": "npm:^1.3.8" "@backstage/errors": "npm:^1.3.1" - "@backstage/frontend-plugin-api": "npm:^0.17.2" + "@backstage/frontend-plugin-api": "npm:^0.17.3" "@backstage/types": "npm:^1.2.2" "@backstage/version-bridge": "npm:^1.0.12" history: "npm:^5.0.0" @@ -2753,23 +2755,23 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/e0b2c69f4ea8b01c1ea35d63237d99f2a13fc4a00312923fd93a4b89645f2e6d62af11c2b6849dfa547025bdfc8f5cf5db9c4bec46dee9ffc08acbce31b6477a + checksum: 10/7e746a509bf684fcf4bcd3e8333af29cb1a135717d259eabf18bafbbdf00a08bbf7c6f429e2b8144db130e464f2cd7e7d40cbea4f6c334c02d4a808c7a025214 languageName: node linkType: hard "@backstage/dev-utils@npm:^1.1.24": - version: 1.1.24 - resolution: "@backstage/dev-utils@npm:1.1.24" + version: 1.1.25 + resolution: "@backstage/dev-utils@npm:1.1.25" dependencies: - "@backstage/app-defaults": "npm:^1.7.9" + "@backstage/app-defaults": "npm:^1.7.10" "@backstage/catalog-model": "npm:^1.9.0" - "@backstage/core-app-api": "npm:^1.20.2" - "@backstage/core-components": "npm:^0.18.11" - "@backstage/core-plugin-api": "npm:^1.12.7" - "@backstage/integration-react": "npm:^1.2.19" - "@backstage/plugin-catalog-react": "npm:^3.1.0" + "@backstage/core-app-api": "npm:^1.20.3" + "@backstage/core-components": "npm:^0.18.12" + "@backstage/core-plugin-api": "npm:^1.12.8" + "@backstage/integration-react": "npm:^1.2.20" + "@backstage/plugin-catalog-react": "npm:^3.2.0" "@backstage/theme": "npm:^0.7.3" - "@backstage/ui": "npm:^0.16.0" + "@backstage/ui": "npm:^0.17.0" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" react-use: "npm:^17.2.4" @@ -2781,7 +2783,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/1e4a1642a67ce03bf968b92ef4b64f2cbd89698a114d5aa40e1e35c98a8c000f31a7ed568f2d1c7f1daebb46becbbd1a64c66287f5065b765c2be9d6ff0e786d + checksum: 10/72434626a55f954ce66e9b981c4f955f0339854881382fe0b38a531890832c761e88ea84d6970fd0528b9e918d78056d8fae1710a1676578a403d74d07c92014 languageName: node linkType: hard @@ -2820,7 +2822,7 @@ __metadata: languageName: node linkType: hard -"@backstage/filter-predicates@npm:^0.1.3, @backstage/filter-predicates@npm:^0.1.4": +"@backstage/filter-predicates@npm:^0.1.4": version: 0.1.4 resolution: "@backstage/filter-predicates@npm:0.1.4" dependencies: @@ -2833,17 +2835,17 @@ __metadata: languageName: node linkType: hard -"@backstage/frontend-app-api@npm:^0.16.4": - version: 0.16.4 - resolution: "@backstage/frontend-app-api@npm:0.16.4" +"@backstage/frontend-app-api@npm:^0.16.6": + version: 0.16.6 + resolution: "@backstage/frontend-app-api@npm:0.16.6" dependencies: "@backstage/config": "npm:^1.3.8" - "@backstage/core-app-api": "npm:^1.20.2" - "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/core-app-api": "npm:^1.20.3" + "@backstage/core-plugin-api": "npm:^1.12.8" "@backstage/errors": "npm:^1.3.1" - "@backstage/filter-predicates": "npm:^0.1.3" - "@backstage/frontend-defaults": "npm:^0.5.3" - "@backstage/frontend-plugin-api": "npm:^0.17.2" + "@backstage/filter-predicates": "npm:^0.1.4" + "@backstage/frontend-defaults": "npm:^0.5.4" + "@backstage/frontend-plugin-api": "npm:^0.17.3" "@backstage/types": "npm:^1.2.2" "@backstage/version-bridge": "npm:^1.0.12" lodash: "npm:^4.17.21" @@ -2856,20 +2858,20 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/cfe08a45202f49ccda9cd7343d96621fcbbf234b86aa912b1a4dfab159d84f023d4074db60f879343e3e8f19a5e837a721c4c0f2e12d31e9e63a59f6b69ead60 + checksum: 10/5e611887a8fcc8facd9c9f66962e72c62a5924645c7292f4f958fc6c03fc16f7152504b0781737fb9c7714e3d58c0e6f30924cabb00eddbc94a7d2024e1039fa languageName: node linkType: hard -"@backstage/frontend-defaults@npm:^0.5.3": - version: 0.5.3 - resolution: "@backstage/frontend-defaults@npm:0.5.3" +"@backstage/frontend-defaults@npm:^0.5.3, @backstage/frontend-defaults@npm:^0.5.4": + version: 0.5.4 + resolution: "@backstage/frontend-defaults@npm:0.5.4" dependencies: "@backstage/config": "npm:^1.3.8" - "@backstage/core-components": "npm:^0.18.11" + "@backstage/core-components": "npm:^0.18.12" "@backstage/errors": "npm:^1.3.1" - "@backstage/frontend-app-api": "npm:^0.16.4" - "@backstage/frontend-plugin-api": "npm:^0.17.2" - "@backstage/plugin-app": "npm:^0.5.0" + "@backstage/frontend-app-api": "npm:^0.16.6" + "@backstage/frontend-plugin-api": "npm:^0.17.3" + "@backstage/plugin-app": "npm:^0.5.1" "@react-hookz/web": "npm:^24.0.0" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 @@ -2879,17 +2881,17 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/a9e1e96d99d242d0b5c1f78db6851848a631935fade083885878be03bd7aca05976084925e0a3dda2c17c8840e4028b1e75ee295b063dd926b37610f7f157826 + checksum: 10/5f9c28c3d0b1b4943ed708cca61748eae1a8d172a7c09215eaa200d43a982184febf1462ba36a4e2ef7d1758f0126dcc2a1213e9ae6c025beed5b48e067d969a languageName: node linkType: hard -"@backstage/frontend-plugin-api@npm:^0.17.2": - version: 0.17.2 - resolution: "@backstage/frontend-plugin-api@npm:0.17.2" +"@backstage/frontend-plugin-api@npm:^0.17.2, @backstage/frontend-plugin-api@npm:^0.17.3": + version: 0.17.3 + resolution: "@backstage/frontend-plugin-api@npm:0.17.3" dependencies: "@backstage/config": "npm:^1.3.8" "@backstage/errors": "npm:^1.3.1" - "@backstage/filter-predicates": "npm:^0.1.3" + "@backstage/filter-predicates": "npm:^0.1.4" "@backstage/types": "npm:^1.2.2" "@backstage/version-bridge": "npm:^1.0.12" "@standard-schema/spec": "npm:^1.1.0" @@ -2903,7 +2905,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/b8991723a01e057dafd37aa412c1253948837dd529cf9edd09bab544fd7c9496659f14299d5060f4bcf39f8fb97023238394d86b9385febf60ce0ddddf64e96d + checksum: 10/16da36c4d922b0ac0d5a146476ad00bc82bb1033a164be5f5f9d282a144c06a5ea7d3a093993133521bbcf865f543194d065079660137931d0f7d5a263bd8472 languageName: node linkType: hard @@ -2922,12 +2924,12 @@ __metadata: languageName: node linkType: hard -"@backstage/integration-react@npm:^1.2.19": - version: 1.2.19 - resolution: "@backstage/integration-react@npm:1.2.19" +"@backstage/integration-react@npm:^1.2.20": + version: 1.2.20 + resolution: "@backstage/integration-react@npm:1.2.20" dependencies: "@backstage/config": "npm:^1.3.8" - "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/core-plugin-api": "npm:^1.12.8" "@backstage/integration": "npm:^2.0.3" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" @@ -2939,7 +2941,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/fe02dc168d20a350e35bb774fc511ef0ebc6ed5d9f4e6371406b65a3bd9b2c8d66bafc831a138ceca11a16368a012810700d4dfd4facae6119b923fdf4caa6b1 + checksum: 10/1587eaab1f6e5efb5fec32061dc7fa53819624d4087f048da57ad73ef7351032bfbf2ff182eadc36ab1e0b30d9a705ebc85c9c1ee373880087feba2e0b338642 languageName: node linkType: hard @@ -2987,12 +2989,12 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-app-react@npm:^0.2.4": - version: 0.2.4 - resolution: "@backstage/plugin-app-react@npm:0.2.4" +"@backstage/plugin-app-react@npm:^0.2.4, @backstage/plugin-app-react@npm:^0.2.5": + version: 0.2.5 + resolution: "@backstage/plugin-app-react@npm:0.2.5" dependencies: - "@backstage/core-plugin-api": "npm:^1.12.7" - "@backstage/frontend-plugin-api": "npm:^0.17.2" + "@backstage/core-plugin-api": "npm:^1.12.8" + "@backstage/frontend-plugin-api": "npm:^0.17.3" "@material-ui/core": "npm:^4.9.13" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 @@ -3002,24 +3004,24 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/82d2f32384796695094344d6eb46e6a8a91266d6598d78338ab3cbcd2fb6d83b75bae59009263bcc564b11ab9b5a91ec5373a466e06cde0f6574f12e4fa1a52e + checksum: 10/e6dd0bd1131dd88c0d589e6f4947ce862942ecade92c421293ca3da2a5011183846bbdd0b27227c1eb6401385d6edf99bc0da90f48a8618dbe9d11e0cf951621 languageName: node linkType: hard -"@backstage/plugin-app@npm:^0.5.0": - version: 0.5.0 - resolution: "@backstage/plugin-app@npm:0.5.0" +"@backstage/plugin-app@npm:^0.5.1": + version: 0.5.1 + resolution: "@backstage/plugin-app@npm:0.5.1" dependencies: - "@backstage/core-components": "npm:^0.18.11" - "@backstage/core-plugin-api": "npm:^1.12.7" - "@backstage/filter-predicates": "npm:^0.1.3" - "@backstage/frontend-plugin-api": "npm:^0.17.2" - "@backstage/integration-react": "npm:^1.2.19" - "@backstage/plugin-app-react": "npm:^0.2.4" - "@backstage/plugin-permission-react": "npm:^0.5.2" + "@backstage/core-components": "npm:^0.18.12" + "@backstage/core-plugin-api": "npm:^1.12.8" + "@backstage/filter-predicates": "npm:^0.1.4" + "@backstage/frontend-plugin-api": "npm:^0.17.3" + "@backstage/integration-react": "npm:^1.2.20" + "@backstage/plugin-app-react": "npm:^0.2.5" + "@backstage/plugin-permission-react": "npm:^0.5.3" "@backstage/theme": "npm:^0.7.3" "@backstage/types": "npm:^1.2.2" - "@backstage/ui": "npm:^0.16.0" + "@backstage/ui": "npm:^0.17.0" "@backstage/version-bridge": "npm:^1.0.12" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" @@ -3040,20 +3042,20 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/e02a863c442172f99ad36263b73a3bddb53c086eb12bd21039aa2b970a49086ff6c09dc1fac8698d92c42c910498a885faf6e6ff7f07b8fa177e2eb1f9d7929c + checksum: 10/bd5ad289f7f465c20e3083b6241a54e54fc5b552e81e5dd0f482d0b85070b7f338aa72ad27545db0caac7e81d67495a0d32fae924cfcb66ba589337ceca0fa9e languageName: node linkType: hard "@backstage/plugin-auth-backend-module-guest-provider@npm:^0.2.20": - version: 0.2.20 - resolution: "@backstage/plugin-auth-backend-module-guest-provider@npm:0.2.20" + version: 0.2.21 + resolution: "@backstage/plugin-auth-backend-module-guest-provider@npm:0.2.21" dependencies: - "@backstage/backend-plugin-api": "npm:^1.9.2" + "@backstage/backend-plugin-api": "npm:^1.9.3" "@backstage/catalog-model": "npm:^1.9.0" "@backstage/errors": "npm:^1.3.1" - "@backstage/plugin-auth-node": "npm:^0.7.2" + "@backstage/plugin-auth-node": "npm:^0.7.3" passport-oauth2: "npm:^1.7.0" - checksum: 10/a8238c1a6214938597844d7d0efaddb112fedb8fed128bc6c1c7d5974dfa9fa9ffe6d4beec8d13a4207ad35cce175931135ee04e020775f2ca349f36f218e52a + checksum: 10/aaacfe6d987a672af03eb8f91078efe99af172f15e90f8a53b86cd0daf99e9fa8764f0776d72072124a6acc65e80f702b9aa51fe185b58596659c4dae54fa672 languageName: node linkType: hard @@ -3105,7 +3107,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-auth-node@npm:^0.7.2, @backstage/plugin-auth-node@npm:^0.7.3": +"@backstage/plugin-auth-node@npm:^0.7.3": version: 0.7.3 resolution: "@backstage/plugin-auth-node@npm:0.7.3" dependencies: @@ -3129,35 +3131,35 @@ __metadata: linkType: hard "@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:^0.2.21": - version: 0.2.21 - resolution: "@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:0.2.21" + version: 0.2.22 + resolution: "@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:0.2.22" dependencies: - "@backstage/backend-plugin-api": "npm:^1.9.2" + "@backstage/backend-plugin-api": "npm:^1.9.3" "@backstage/catalog-model": "npm:^1.9.0" "@backstage/plugin-catalog-common": "npm:^1.1.10" - "@backstage/plugin-catalog-node": "npm:^2.2.2" + "@backstage/plugin-catalog-node": "npm:^2.2.3" "@backstage/plugin-scaffolder-common": "npm:^2.2.1" - checksum: 10/1e656f68eef0a42742a412b96177e5fcf43bb0a654da5d9aef4cc8af126aebeb870520c545cec4810f8fb49d044d07df9abf5099dc94550e8dcb537c02760efc + checksum: 10/6aa8a5b991a660ddf21c628fb6655d833a7670f2c926286b619913f376f1f96b793a3632cef88740d711896924f6d7dddb79016c29def5dfd4053a72abc1d3f0 languageName: node linkType: hard "@backstage/plugin-catalog-backend@npm:^3.8.0": - version: 3.8.0 - resolution: "@backstage/plugin-catalog-backend@npm:3.8.0" + version: 3.8.1 + resolution: "@backstage/plugin-catalog-backend@npm:3.8.1" dependencies: - "@backstage/backend-openapi-utils": "npm:^0.6.10" - "@backstage/backend-plugin-api": "npm:^1.9.2" - "@backstage/catalog-client": "npm:^1.16.0" + "@backstage/backend-openapi-utils": "npm:^0.7.0" + "@backstage/backend-plugin-api": "npm:^1.9.3" + "@backstage/catalog-client": "npm:^1.16.1" "@backstage/catalog-model": "npm:^1.9.0" "@backstage/config": "npm:^1.3.8" "@backstage/errors": "npm:^1.3.1" - "@backstage/filter-predicates": "npm:^0.1.3" + "@backstage/filter-predicates": "npm:^0.1.4" "@backstage/integration": "npm:^2.0.3" "@backstage/plugin-catalog-common": "npm:^1.1.10" - "@backstage/plugin-catalog-node": "npm:^2.2.2" - "@backstage/plugin-events-node": "npm:^0.4.23" + "@backstage/plugin-catalog-node": "npm:^2.2.3" + "@backstage/plugin-events-node": "npm:^0.4.24" "@backstage/plugin-permission-common": "npm:^0.9.9" - "@backstage/plugin-permission-node": "npm:^0.11.1" + "@backstage/plugin-permission-node": "npm:^0.11.2" "@backstage/types": "npm:^1.2.2" "@opentelemetry/api": "npm:^1.9.0" ajv: "npm:^8.10.0" @@ -3178,8 +3180,8 @@ __metadata: yaml: "npm:^2.0.0" yn: "npm:^4.0.0" zod: "npm:^3.25.76 || ^4.0.0" - zod-validation-error: "npm:^4.0.2" - checksum: 10/d54d05430942b177ced37d955dd9c93ae2093176cafb91849299aeafddb69c4cb8a8727d159176154dc36b819cceac39a205e451bd200cfe92e542ae50573667 + zod-validation-error: "npm:^5.0.0" + checksum: 10/9bdf457d15a3f12706cc483bdd388863b36cc15a456f1e529e0722c5c23677581fc599526835f49e6e654dd82d7f29fb52232247dd603fc0c1aa2e555b9625c3 languageName: node linkType: hard @@ -3218,23 +3220,23 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog-react@npm:^3.1.0": - version: 3.1.0 - resolution: "@backstage/plugin-catalog-react@npm:3.1.0" +"@backstage/plugin-catalog-react@npm:^3.2.0": + version: 3.2.0 + resolution: "@backstage/plugin-catalog-react@npm:3.2.0" dependencies: - "@backstage/catalog-client": "npm:^1.16.0" + "@backstage/catalog-client": "npm:^1.16.1" "@backstage/catalog-model": "npm:^1.9.0" - "@backstage/core-compat-api": "npm:^0.5.12" - "@backstage/core-components": "npm:^0.18.11" - "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/core-compat-api": "npm:^0.5.13" + "@backstage/core-components": "npm:^0.18.12" + "@backstage/core-plugin-api": "npm:^1.12.8" "@backstage/errors": "npm:^1.3.1" - "@backstage/filter-predicates": "npm:^0.1.3" - "@backstage/frontend-plugin-api": "npm:^0.17.2" - "@backstage/integration-react": "npm:^1.2.19" + "@backstage/filter-predicates": "npm:^0.1.4" + "@backstage/frontend-plugin-api": "npm:^0.17.3" + "@backstage/integration-react": "npm:^1.2.20" "@backstage/plugin-permission-common": "npm:^0.9.9" - "@backstage/plugin-permission-react": "npm:^0.5.2" + "@backstage/plugin-permission-react": "npm:^0.5.3" "@backstage/types": "npm:^1.2.2" - "@backstage/ui": "npm:^0.16.0" + "@backstage/ui": "npm:^0.17.0" "@backstage/version-bridge": "npm:^1.0.12" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" @@ -3250,7 +3252,7 @@ __metadata: zen-observable: "npm:^0.10.0" zod: "npm:^4.0.0" peerDependencies: - "@backstage/frontend-test-utils": ^0.6.1 + "@backstage/frontend-test-utils": ^0.6.2 "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 @@ -3260,32 +3262,32 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10/0797d0769902b8d61249e216722113731bb52d0d85574d16cef367e5e4e07ffb6e6a0598e759a59fc4dd676c07c1b541048b6224f70a70b5cf8e4118bc6e0f55 + checksum: 10/1ee6dae21c995fb38ce4fdbf6d4a8155d22d7ba531e8794d244acc3020137e647103e2dd78e61398cc28b70f19f178b58d612cb1b68aed59b58e51f4498caac4 languageName: node linkType: hard "@backstage/plugin-catalog@npm:^2.0.6": - version: 2.0.6 - resolution: "@backstage/plugin-catalog@npm:2.0.6" + version: 2.0.7 + resolution: "@backstage/plugin-catalog@npm:2.0.7" dependencies: - "@backstage/catalog-client": "npm:^1.16.0" + "@backstage/catalog-client": "npm:^1.16.1" "@backstage/catalog-model": "npm:^1.9.0" - "@backstage/core-compat-api": "npm:^0.5.12" - "@backstage/core-components": "npm:^0.18.11" - "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/core-compat-api": "npm:^0.5.13" + "@backstage/core-components": "npm:^0.18.12" + "@backstage/core-plugin-api": "npm:^1.12.8" "@backstage/errors": "npm:^1.3.1" - "@backstage/frontend-plugin-api": "npm:^0.17.2" - "@backstage/integration-react": "npm:^1.2.19" + "@backstage/frontend-plugin-api": "npm:^0.17.3" + "@backstage/integration-react": "npm:^1.2.20" "@backstage/plugin-catalog-common": "npm:^1.1.10" - "@backstage/plugin-catalog-react": "npm:^3.1.0" - "@backstage/plugin-permission-react": "npm:^0.5.2" + "@backstage/plugin-catalog-react": "npm:^3.2.0" + "@backstage/plugin-permission-react": "npm:^0.5.3" "@backstage/plugin-scaffolder-common": "npm:^2.2.1" "@backstage/plugin-search-common": "npm:^1.2.24" - "@backstage/plugin-search-react": "npm:^1.11.5" + "@backstage/plugin-search-react": "npm:^1.11.6" "@backstage/plugin-techdocs-common": "npm:^0.1.1" - "@backstage/plugin-techdocs-react": "npm:^1.3.12" + "@backstage/plugin-techdocs-react": "npm:^1.3.13" "@backstage/types": "npm:^1.2.2" - "@backstage/ui": "npm:^0.16.0" + "@backstage/ui": "npm:^0.17.0" "@backstage/version-bridge": "npm:^1.0.12" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" @@ -3309,15 +3311,15 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/29ac88dff8ad173cedbadf56124c6a96ff34937dee8840e727246105fae3f8024da8f849d40386dfbe2ef87a7eb5aa5e94a7341ba70be386a35b54252f47a5b2 + checksum: 10/0f7ded9223137590f28e3a1ff421c9121212c8d215270bf12f7009ea4fef56d7933193c434863208fe56edbd177d11d9118c59a3686e18f160f7db1286d56cc1 languageName: node linkType: hard -"@backstage/plugin-events-node@npm:^0.4.23": - version: 0.4.23 - resolution: "@backstage/plugin-events-node@npm:0.4.23" +"@backstage/plugin-events-node@npm:^0.4.24": + version: 0.4.24 + resolution: "@backstage/plugin-events-node@npm:0.4.24" dependencies: - "@backstage/backend-plugin-api": "npm:^1.9.2" + "@backstage/backend-plugin-api": "npm:^1.9.3" "@backstage/errors": "npm:^1.3.1" "@backstage/types": "npm:^1.2.2" "@types/content-type": "npm:^1.1.8" @@ -3326,26 +3328,26 @@ __metadata: cross-fetch: "npm:^4.0.0" express: "npm:^4.22.0" uri-template: "npm:^2.0.0" - checksum: 10/3eef8215b0c51df85c3773bc39eb87bccb28b7e2b1dd4245e64eb7499a7648b2d9a93a76518e7cd416d017e01ab5147948e888993a050aaa13097e80b2280397 + checksum: 10/e36fb85c8d3cdb0781d9ed92050432f139925e3828912ac6ce1202be65540552db2b569b693015d54c26c635318a32a5ed550954aef2b4d3c5450e240e0208fe languageName: node linkType: hard "@backstage/plugin-permission-backend@npm:^0.7.13": - version: 0.7.13 - resolution: "@backstage/plugin-permission-backend@npm:0.7.13" + version: 0.7.14 + resolution: "@backstage/plugin-permission-backend@npm:0.7.14" dependencies: - "@backstage/backend-plugin-api": "npm:^1.9.2" + "@backstage/backend-plugin-api": "npm:^1.9.3" "@backstage/config": "npm:^1.3.8" "@backstage/errors": "npm:^1.3.1" - "@backstage/plugin-auth-node": "npm:^0.7.2" + "@backstage/plugin-auth-node": "npm:^0.7.3" "@backstage/plugin-permission-common": "npm:^0.9.9" - "@backstage/plugin-permission-node": "npm:^0.11.1" + "@backstage/plugin-permission-node": "npm:^0.11.2" dataloader: "npm:^2.0.0" express: "npm:^4.22.0" express-promise-router: "npm:^4.1.0" lodash: "npm:^4.17.21" zod: "npm:^3.25.76 || ^4.0.0" - checksum: 10/20e35343de1cb182aad85077cf32f99ae00f0034b1dae2f7fe348426f6a46e0a7b0b4df568097d87e5240aae5af660294245734152d40a62795516577974ae35 + checksum: 10/437cd998c342cc498a0efa5a9d44e4d77941e4e2bb576d7c84f40803ad967ee06372e6cda834add32524111d7608cbe03cd764806edf27f0bec1c12c6be4a8c0 languageName: node linkType: hard @@ -3380,12 +3382,12 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-permission-react@npm:^0.5.2": - version: 0.5.2 - resolution: "@backstage/plugin-permission-react@npm:0.5.2" +"@backstage/plugin-permission-react@npm:^0.5.2, @backstage/plugin-permission-react@npm:^0.5.3": + version: 0.5.3 + resolution: "@backstage/plugin-permission-react@npm:0.5.3" dependencies: "@backstage/config": "npm:^1.3.8" - "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/core-plugin-api": "npm:^1.12.8" "@backstage/plugin-permission-common": "npm:^0.9.9" dataloader: "npm:^2.0.0" swr: "npm:^2.0.0" @@ -3396,7 +3398,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/3c147b6d494794298f9f5f2abfb1d7ba79e97ae18bfaa9d0451ac4dbd2600165eac59d41032eeedca28376cf3a5701e864451b9d1d4ab95c1d634bce09fd0ed0 + checksum: 10/7e78116d237ede65fccc12ff72cd6bafff706bd62dce3fc16ff7a62759aa6dbd0ae8ebcef8f6a6f8b4be76a0d67ed65771df3c3f73d83b3899867d4853354ba6 languageName: node linkType: hard @@ -3428,13 +3430,13 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-react@npm:^1.11.5": - version: 1.11.5 - resolution: "@backstage/plugin-search-react@npm:1.11.5" +"@backstage/plugin-search-react@npm:^1.11.6": + version: 1.11.6 + resolution: "@backstage/plugin-search-react@npm:1.11.6" dependencies: - "@backstage/core-components": "npm:^0.18.11" - "@backstage/core-plugin-api": "npm:^1.12.7" - "@backstage/frontend-plugin-api": "npm:^0.17.2" + "@backstage/core-components": "npm:^0.18.12" + "@backstage/core-plugin-api": "npm:^1.12.8" + "@backstage/frontend-plugin-api": "npm:^0.17.3" "@backstage/plugin-search-common": "npm:^1.2.24" "@backstage/theme": "npm:^0.7.3" "@backstage/types": "npm:^1.2.2" @@ -3454,7 +3456,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/3e626dea094954ed16cb6990e95a2e49c2c435f595a06f4eba0524389a54ca17fc79760ba87bba45de2a266d13c2a5bacabec473212d6bb027da0e905163b989 + checksum: 10/6192c21ed2be7773edb0aeef803961991a0c6892a8c24e21c5622a08fdf01d1688a93fe0626d315acdfe67265b689584ab570c028df547a0390ef7101cb7fdbc languageName: node linkType: hard @@ -3465,15 +3467,15 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-techdocs-react@npm:^1.3.12": - version: 1.3.12 - resolution: "@backstage/plugin-techdocs-react@npm:1.3.12" +"@backstage/plugin-techdocs-react@npm:^1.3.13": + version: 1.3.13 + resolution: "@backstage/plugin-techdocs-react@npm:1.3.13" dependencies: "@backstage/catalog-model": "npm:^1.9.0" "@backstage/config": "npm:^1.3.8" - "@backstage/core-components": "npm:^0.18.11" - "@backstage/core-plugin-api": "npm:^1.12.7" - "@backstage/frontend-plugin-api": "npm:^0.17.2" + "@backstage/core-components": "npm:^0.18.12" + "@backstage/core-plugin-api": "npm:^1.12.8" + "@backstage/frontend-plugin-api": "npm:^0.17.3" "@backstage/plugin-techdocs-common": "npm:^0.1.1" "@backstage/version-bridge": "npm:^1.0.12" "@material-ui/core": "npm:^4.12.2" @@ -3489,7 +3491,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/b3b25cb27ac5a9261d2a29df0b880a6db922cb40544086c36095cdd5bb8a69558046b5ad0b9865351a9465da0ed2c3a3bb1343cfa5b8abe89c99459bbfc6837c + checksum: 10/e74573d9fc564a1044ea797d05dab2e915b0f7ad79742d3103b2e0d4be3ee001c1c2a358256c6aeff194885560229213ae50b1e64508a956c172ad848b706d7f languageName: node linkType: hard @@ -3568,14 +3570,14 @@ __metadata: linkType: hard "@backstage/test-utils@npm:^1.7.19": - version: 1.7.19 - resolution: "@backstage/test-utils@npm:1.7.19" + version: 1.7.20 + resolution: "@backstage/test-utils@npm:1.7.20" dependencies: "@backstage/config": "npm:^1.3.8" - "@backstage/core-app-api": "npm:^1.20.2" - "@backstage/core-plugin-api": "npm:^1.12.7" + "@backstage/core-app-api": "npm:^1.20.3" + "@backstage/core-plugin-api": "npm:^1.12.8" "@backstage/plugin-permission-common": "npm:^0.9.9" - "@backstage/plugin-permission-react": "npm:^0.5.2" + "@backstage/plugin-permission-react": "npm:^0.5.3" "@backstage/theme": "npm:^0.7.3" "@backstage/types": "npm:^1.2.2" "@material-ui/core": "npm:^4.12.2" @@ -3595,7 +3597,7 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10/ee5831bf7293c7e4866ec8e29d20604edfd686c3431659d4bc2aedff934ce9b96c05dcbb1e404614c6faf7630c5c82b2ca2c5b4ac7879404db61e78927f872be + checksum: 10/62b15167430ab5d7e73b0fba13cc40488fd6899ea22620e90fcb31509bc24416e934e3fe925dbea88534d31e8bef73e745843a804659218998ab0d9f5494ba9f languageName: node linkType: hard @@ -3653,6 +3655,33 @@ __metadata: languageName: node linkType: hard +"@backstage/ui@npm:^0.17.0": + version: 0.17.0 + resolution: "@backstage/ui@npm:0.17.0" + dependencies: + "@backstage/version-bridge": "npm:^1.0.12" + "@braintree/sanitize-url": "npm:^7.1.2" + "@internationalized/date": "npm:^3.12.0" + "@remixicon/react": "npm:>=4.6.0 <4.9.0" + "@tanstack/react-table": "npm:^8.21.3" + clsx: "npm:^2.1.1" + marked: "npm:^15.0.12" + react-aria: "npm:~3.48.0" + react-aria-components: "npm:~1.17.0" + react-stately: "npm:~3.46.0" + use-sync-external-store: "npm:^1.4.0" + peerDependencies: + "@types/react": ^18.0.0 + react: ^18.0.0 + react-dom: ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/62282be4e2b3361d58236d8d0bbffa328ef875bdd35ac19f3764edb2799f55e23d426b3deb844d622a39e092f63c8abb5947b98e955c1cdb18388d1c853059f3 + languageName: node + linkType: hard + "@backstage/version-bridge@npm:^1.0.12": version: 1.0.12 resolution: "@backstage/version-bridge@npm:1.0.12" @@ -3946,15 +3975,6 @@ __metadata: languageName: node linkType: hard -"@cspotcode/source-map-support@npm:^0.8.0": - version: 0.8.1 - resolution: "@cspotcode/source-map-support@npm:0.8.1" - dependencies: - "@jridgewell/trace-mapping": "npm:0.3.9" - checksum: 10/b6e38a1712fab242c86a241c229cf562195aad985d0564bd352ac404be583029e89e93028ffd2c251d2c407ecac5fb0cbdca94a2d5c10f29ac806ede0508b3ff - languageName: node - linkType: hard - "@csstools/color-helpers@npm:^6.0.2": version: 6.0.2 resolution: "@csstools/color-helpers@npm:6.0.2" @@ -4803,15 +4823,17 @@ __metadata: dependenciesMeta: better-sqlite3: built: true + isolated-vm: + built: true languageName: unknown linkType: soft "@internationalized/date@npm:^3.12.0, @internationalized/date@npm:^3.12.1": - version: 3.12.2 - resolution: "@internationalized/date@npm:3.12.2" + version: 3.12.3 + resolution: "@internationalized/date@npm:3.12.3" dependencies: "@swc/helpers": "npm:^0.5.0" - checksum: 10/5a01927e21eb51756a006976a96a1dd499099ba8d2457437a09762f9682f7606e9dd4d4bb87165c9357dc0488fc2c9b5212e55de4d9115dd72087aad1aa9ad87 + checksum: 10/2c4ce86ac5aec2f9f6799cd455b776ab8750238059d1ed031f866c1f7bd7799fc6f938d46a328066989e1f24f5f9c10d4bfad3b2cab45fba0ce8fbb369ef7e1e languageName: node linkType: hard @@ -5235,7 +5257,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": +"@jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.2 resolution: "@jridgewell/resolve-uri@npm:3.1.2" checksum: 10/97106439d750a409c22c8bff822d648f6a71f3aa9bc8e5129efdc36343cd3096ddc4eeb1c62d2fe48e9bdd4db37b05d4646a17114ecebd3bbcacfa2de51c3c1d @@ -5252,23 +5274,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15, @jridgewell/sourcemap-codec@npm:^1.5.0": +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15, @jridgewell/sourcemap-codec@npm:^1.5.0": version: 1.5.0 resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" checksum: 10/4ed6123217569a1484419ac53f6ea0d9f3b57e5b57ab30d7c267bdb27792a27eb0e4b08e84a2680aa55cc2f2b411ffd6ec3db01c44fdc6dc43aca4b55f8374fd languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:0.3.9": - version: 0.3.9 - resolution: "@jridgewell/trace-mapping@npm:0.3.9" - dependencies: - "@jridgewell/resolve-uri": "npm:^3.0.3" - "@jridgewell/sourcemap-codec": "npm:^1.4.10" - checksum: 10/83deafb8e7a5ca98993c2c6eeaa93c270f6f647a4c0dc00deb38c9cf9b2d3b7bf15e8839540155247ef034a052c0ec4466f980bf0c9e2ab63b97d16c0cedd3ff - languageName: node - linkType: hard - "@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": version: 0.3.31 resolution: "@jridgewell/trace-mapping@npm:0.3.31" @@ -5743,75 +5755,70 @@ __metadata: languageName: node linkType: hard -"@module-federation/bridge-react-webpack-plugin@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/bridge-react-webpack-plugin@npm:2.6.0" +"@module-federation/bridge-react-webpack-plugin@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/bridge-react-webpack-plugin@npm:2.8.2" dependencies: - "@module-federation/sdk": "npm:2.6.0" - "@types/semver": "npm:7.5.8" - semver: "npm:7.6.3" - checksum: 10/6d5da3deb4aa385136d74ad649d70cee41c1b77a815b15464ffc06bb079f84d16252a4bd7c8eeb4834ab9aaa1ac2370d249233d9e45433f46b7eab5d58dc621a + "@module-federation/sdk": "npm:2.8.2" + checksum: 10/b0b2c44c4223c09b668770a54dd22606ac1080a957d5243e0989ef35b8a81a6380e63f7a08ac434cd292dd303dc114b083385454bb11240b2aede7c15dd0f496 languageName: node linkType: hard -"@module-federation/cli@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/cli@npm:2.6.0" +"@module-federation/cli@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/cli@npm:2.8.2" dependencies: - "@module-federation/dts-plugin": "npm:2.6.0" - "@module-federation/sdk": "npm:2.6.0" + "@module-federation/dts-plugin": "npm:2.8.2" + "@module-federation/sdk": "npm:2.8.2" commander: "npm:11.1.0" jiti: "npm:2.4.2" bin: mf: bin/mf.js - checksum: 10/6ff8f83cc368d199791b04743989c26d481a0fe9f2c7750a96563eee2d77a30a583d7310d6b8082c85e6619aafebbbc5cf1dda604b153c852fe928a49f36ca0c + checksum: 10/08fe421786df31230bd490af04fc3d8efb837f9ddec9b22e8be8a9bfecc5f82f757bb0b44d4ef0e3a77ced7d6a5488d47a4017eafabaf5efce74db62aa706e78 languageName: node linkType: hard -"@module-federation/dts-plugin@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/dts-plugin@npm:2.6.0" - dependencies: - "@module-federation/error-codes": "npm:2.6.0" - "@module-federation/managers": "npm:2.6.0" - "@module-federation/sdk": "npm:2.6.0" - "@module-federation/third-party-dts-extractor": "npm:2.6.0" - adm-zip: "npm:0.5.10" - ansi-colors: "npm:4.1.3" +"@module-federation/dts-plugin@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/dts-plugin@npm:2.8.2" + dependencies: + "@module-federation/error-codes": "npm:2.8.2" + "@module-federation/managers": "npm:2.8.2" + "@module-federation/sdk": "npm:2.8.2" + "@module-federation/third-party-dts-extractor": "npm:2.8.2" + adm-zip: "npm:0.6.0" isomorphic-ws: "npm:5.0.0" - node-schedule: "npm:2.1.1" - undici: "npm:7.28.0" + undici: "npm:7.29.0" ws: "npm:8.21.0" peerDependencies: - typescript: ^4.9.0 || ^5.0.0 + typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 vue-tsc: ">=1.0.24" peerDependenciesMeta: vue-tsc: optional: true - checksum: 10/35e29161ebe375d5bddd60ec74478e38fc8b8e7aa6fe5bd03569b3fc16be9e0273e6b43c47d831a66b7dab03ec4712e063c96d249b2acc93d4b9d84ac93f7a3f + checksum: 10/f326d9c8f14bf4aec6c41aec6bcbe064fb912fbbb008f980d6993e9796497aae13eb0fce877486bff32abdc757425bc0685ce68b4920d9a1553b64b1300fb1c8 languageName: node linkType: hard "@module-federation/enhanced@npm:^2.3.3": - version: 2.6.0 - resolution: "@module-federation/enhanced@npm:2.6.0" - dependencies: - "@module-federation/bridge-react-webpack-plugin": "npm:2.6.0" - "@module-federation/cli": "npm:2.6.0" - "@module-federation/dts-plugin": "npm:2.6.0" - "@module-federation/error-codes": "npm:2.6.0" - "@module-federation/inject-external-runtime-core-plugin": "npm:2.6.0" - "@module-federation/managers": "npm:2.6.0" - "@module-federation/manifest": "npm:2.6.0" - "@module-federation/rspack": "npm:2.6.0" - "@module-federation/runtime-tools": "npm:2.6.0" - "@module-federation/sdk": "npm:2.6.0" - "@module-federation/webpack-bundler-runtime": "npm:2.6.0" + version: 2.8.2 + resolution: "@module-federation/enhanced@npm:2.8.2" + dependencies: + "@module-federation/bridge-react-webpack-plugin": "npm:2.8.2" + "@module-federation/cli": "npm:2.8.2" + "@module-federation/dts-plugin": "npm:2.8.2" + "@module-federation/error-codes": "npm:2.8.2" + "@module-federation/inject-external-runtime-core-plugin": "npm:2.8.2" + "@module-federation/managers": "npm:2.8.2" + "@module-federation/manifest": "npm:2.8.2" + "@module-federation/rspack": "npm:2.8.2" + "@module-federation/runtime-tools": "npm:2.8.2" + "@module-federation/sdk": "npm:2.8.2" + "@module-federation/webpack-bundler-runtime": "npm:2.8.2" schema-utils: "npm:4.3.0" tapable: "npm:2.3.0" - upath: "npm:2.0.1" peerDependencies: - typescript: ^4.9.0 || ^5.0.0 + typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 vue-tsc: ">=1.0.24" webpack: ^5.0.0 peerDependenciesMeta: @@ -5823,7 +5830,7 @@ __metadata: optional: true bin: mf: bin/mf.js - checksum: 10/cdfc388e9889d8b3726350c5df65718270365a5d2aa72c5d49229da7e13d0ccbd210b1b7f5fd17cffb45535e60860e1545de79c27b9194a7a31b285bb4333df6 + checksum: 10/41ea5399e551796c9d69f0680a1391b632e3d9ef4424a25802b57cce1758bb9f3d15f5becaca6e86d1d42e0df952f9eff0a6ec6c34d4c42402a3895b2ca246d1 languageName: node linkType: hard @@ -5834,65 +5841,63 @@ __metadata: languageName: node linkType: hard -"@module-federation/error-codes@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/error-codes@npm:2.6.0" - checksum: 10/6f6750ac49bcb0d4ae9df78e9a14465a4a605a834112ef0774ad22284c770702414567d8c89fe9fa550723c5db73f1b70a442c8bc3ecef642e9b481b4ff11703 +"@module-federation/error-codes@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/error-codes@npm:2.8.2" + checksum: 10/81d70499d6855d0779023778f8b7e0c9175453172ca27c382f9d4fd51f17391db48c0747f0946a0dc22d5f9b3ecc4e5d0baddfa696242f7e5e09c4e388d7e166 languageName: node linkType: hard -"@module-federation/inject-external-runtime-core-plugin@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/inject-external-runtime-core-plugin@npm:2.6.0" +"@module-federation/inject-external-runtime-core-plugin@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/inject-external-runtime-core-plugin@npm:2.8.2" peerDependencies: - "@module-federation/runtime-tools": 2.6.0 - checksum: 10/35e6a54f836520f0a569b8bc6535c0ad1db2b36d2e9550add055704dd57bf0ce53e878ba06c672a8f058daf3a15c5818c396f0da04c8a15347729d0d932ea6f7 + "@module-federation/runtime-tools": 2.8.2 + checksum: 10/a997a47f17316efa74fb6d365b773e669cf3c83c94f0a6fe4804b3246a4b1b1b368374a49b9a3e99f1e09a2474879472571282dc5e0c1f9f4744d15bce832577 languageName: node linkType: hard -"@module-federation/managers@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/managers@npm:2.6.0" +"@module-federation/managers@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/managers@npm:2.8.2" dependencies: - "@module-federation/sdk": "npm:2.6.0" - find-pkg: "npm:2.0.0" - checksum: 10/8ef8622366194b9e0a8caed19944281510a532500fae1a9863e8552670ff6cf684f70280daff2cf960a40ea5bd7f880367aeced8840ebce78443c4d06af893fe + "@module-federation/sdk": "npm:2.8.2" + checksum: 10/abf7ec3231125d7134faeae7395a9d9da4a1e5bc843e104666e30cd2f061c1c3afe9c2c34e7d547954164cd9ba9e24dbc29cfc08babd3dccd0a04d7e5f4758dd languageName: node linkType: hard -"@module-federation/manifest@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/manifest@npm:2.6.0" +"@module-federation/manifest@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/manifest@npm:2.8.2" dependencies: - "@module-federation/dts-plugin": "npm:2.6.0" - "@module-federation/managers": "npm:2.6.0" - "@module-federation/sdk": "npm:2.6.0" - find-pkg: "npm:2.0.0" - checksum: 10/ecb2e5d0b2248569cf3b5e6678cb87263f53e6efd2ec27acd0d5d02ea708574fc30427b230aeb771f192d9e317c750f3308bbbca2cd9dbb3658a442646c8e7d3 + "@module-federation/dts-plugin": "npm:2.8.2" + "@module-federation/managers": "npm:2.8.2" + "@module-federation/sdk": "npm:2.8.2" + checksum: 10/f6792351664b74e9195b4d966250faa46277d779bfdf2ba4c8903e7f47bb03c98c47a0707b174cc07a1029a023bc462ee293ba14cf641df670c955d3271a494c languageName: node linkType: hard -"@module-federation/rspack@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/rspack@npm:2.6.0" +"@module-federation/rspack@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/rspack@npm:2.8.2" dependencies: - "@module-federation/bridge-react-webpack-plugin": "npm:2.6.0" - "@module-federation/dts-plugin": "npm:2.6.0" - "@module-federation/inject-external-runtime-core-plugin": "npm:2.6.0" - "@module-federation/managers": "npm:2.6.0" - "@module-federation/manifest": "npm:2.6.0" - "@module-federation/runtime-tools": "npm:2.6.0" - "@module-federation/sdk": "npm:2.6.0" + "@module-federation/bridge-react-webpack-plugin": "npm:2.8.2" + "@module-federation/dts-plugin": "npm:2.8.2" + "@module-federation/inject-external-runtime-core-plugin": "npm:2.8.2" + "@module-federation/managers": "npm:2.8.2" + "@module-federation/manifest": "npm:2.8.2" + "@module-federation/runtime-tools": "npm:2.8.2" + "@module-federation/sdk": "npm:2.8.2" peerDependencies: "@rspack/core": ^0.7.0 || ^1.0.0 || ^2.0.0-0 - typescript: ^4.9.0 || ^5.0.0 + typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 vue-tsc: ">=1.0.24" peerDependenciesMeta: typescript: optional: true vue-tsc: optional: true - checksum: 10/0fcab255dce54fdcc4a5e89224fd22db0c72c7d335e71887c2f3827811770920576347853aa10679b03d22a213cd0010b8bc288d30653c456b8944152ec2dc2b + checksum: 10/cccce9839770c79989157d7f69f830d8ed737c81b20f27f41391271d917e6674b0f3753417385524024c0544e0a53fecc9eef8eaeada3740b280c6da9c64ca7f languageName: node linkType: hard @@ -5906,13 +5911,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-core@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/runtime-core@npm:2.6.0" +"@module-federation/runtime-core@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/runtime-core@npm:2.8.2" dependencies: - "@module-federation/error-codes": "npm:2.6.0" - "@module-federation/sdk": "npm:2.6.0" - checksum: 10/9fd4d681b618348f37acfb4709cfd6a6acb12e08ad36587dff3f3e64060abc4cf3e3826fbe5d4a754789d1ae3048c575387ec652e6d18716cfc88de5ff78dccf + "@module-federation/error-codes": "npm:2.8.2" + "@module-federation/sdk": "npm:2.8.2" + checksum: 10/7899271733facc96351001eee107cd41c684ea0602cf772596e7043b38201d665926fc6a4e7a5b7a3dd99eea6f6e06c60bbd229b557a6bb70f53027228a0636d languageName: node linkType: hard @@ -5926,13 +5931,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-tools@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/runtime-tools@npm:2.6.0" +"@module-federation/runtime-tools@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/runtime-tools@npm:2.8.2" dependencies: - "@module-federation/runtime": "npm:2.6.0" - "@module-federation/webpack-bundler-runtime": "npm:2.6.0" - checksum: 10/1cd90ef21c1c51783924360d1686d6ee97a58f29e723128da4f26abbbf804dd4f3297ba3f8330bb56a02070e382db916a056661239dfb64e2a630a3fee306a74 + "@module-federation/runtime": "npm:2.8.2" + "@module-federation/webpack-bundler-runtime": "npm:2.8.2" + checksum: 10/b1af8d2a3339696b27e2f9c2325f598c9d0676a737c225203adb3c77024ba0b1e2ea38d88b76b8e00ee5fdcc4395040ec698c714c09708c3d0c468a367e280c7 languageName: node linkType: hard @@ -5947,14 +5952,14 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime@npm:2.6.0, @module-federation/runtime@npm:^2.3.3": - version: 2.6.0 - resolution: "@module-federation/runtime@npm:2.6.0" +"@module-federation/runtime@npm:2.8.2, @module-federation/runtime@npm:^2.3.3": + version: 2.8.2 + resolution: "@module-federation/runtime@npm:2.8.2" dependencies: - "@module-federation/error-codes": "npm:2.6.0" - "@module-federation/runtime-core": "npm:2.6.0" - "@module-federation/sdk": "npm:2.6.0" - checksum: 10/365d4a06e682cda6c0b8df57dfe0da5976f33e9b588499aad1abaf1613618ab2af6c1f87aacb7d1821764a3f6a8f640b6df01406ebde3bae532bf61e4cd25f84 + "@module-federation/error-codes": "npm:2.8.2" + "@module-federation/runtime-core": "npm:2.8.2" + "@module-federation/sdk": "npm:2.8.2" + checksum: 10/356ef46ffcaf173efdfbe82ed14f7f94a20d961c97c058580f455503ee4003fd69a9a67e7387b92ad600569fa659d808e9867ae2b370789cb4e781b9072c516c languageName: node linkType: hard @@ -5965,25 +5970,17 @@ __metadata: languageName: node linkType: hard -"@module-federation/sdk@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/sdk@npm:2.6.0" - peerDependencies: - node-fetch: ^2.7.0 || ^3.3.2 - peerDependenciesMeta: - node-fetch: - optional: true - checksum: 10/48cf1f30a238c71250c29b6a8f1a4cd7b81b1981635f56edd063f4635609a8f949549f35a2e7bf7365bae60baa611df45ea90c62d836219931ba97ee6a78eb73 +"@module-federation/sdk@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/sdk@npm:2.8.2" + checksum: 10/11dd8653499e702d74dcea03352267cbd39d82aa2a429186ef15912f98fe719ccf7a7f63d4ececce8f7a3d3df73903991feeb80fe45a65016bcf733697215ffa languageName: node linkType: hard -"@module-federation/third-party-dts-extractor@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/third-party-dts-extractor@npm:2.6.0" - dependencies: - find-pkg: "npm:2.0.0" - resolve: "npm:1.22.8" - checksum: 10/380368eb614f6d0e15a1e3dc13c09d9986947baabee7ff970928b4fffc3144f2a96691a094d6d69ad37d5b6799b246d87cf6f5c1774d2c8ffa5a13355366fcc2 +"@module-federation/third-party-dts-extractor@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/third-party-dts-extractor@npm:2.8.2" + checksum: 10/8db40f3fc73dfcb2e0d6412c79c386a697b1ff6ef25b85e425bbfa22597dcd0235122de7ac035096b0d7572c43f462024bd63ca4e5f4db1fbaafb0a87ee3e8f2 languageName: node linkType: hard @@ -5997,14 +5994,14 @@ __metadata: languageName: node linkType: hard -"@module-federation/webpack-bundler-runtime@npm:2.6.0": - version: 2.6.0 - resolution: "@module-federation/webpack-bundler-runtime@npm:2.6.0" +"@module-federation/webpack-bundler-runtime@npm:2.8.2": + version: 2.8.2 + resolution: "@module-federation/webpack-bundler-runtime@npm:2.8.2" dependencies: - "@module-federation/error-codes": "npm:2.6.0" - "@module-federation/runtime": "npm:2.6.0" - "@module-federation/sdk": "npm:2.6.0" - checksum: 10/3aa418ff7ac59aeb7d77b8384d198c9e3b994d9a412aea9574a197f93ebe866cbc7251147966f6251a109fec126fdee99995def19e4f5abe27afb343f9402b22 + "@module-federation/error-codes": "npm:2.8.2" + "@module-federation/runtime": "npm:2.8.2" + "@module-federation/sdk": "npm:2.8.2" + checksum: 10/63474c6057af511d9af77f5896a772e34ba03a7c00c147a5abe654d9c3a1fef3573723535e45a2dcacb417a9b06b2a59538d8fedcc634e349338761e087f04b6 languageName: node linkType: hard @@ -8498,13 +8495,6 @@ __metadata: languageName: node linkType: hard -"@sqltools/formatter@npm:^1.2.5": - version: 1.2.5 - resolution: "@sqltools/formatter@npm:1.2.5" - checksum: 10/ce9335025cd033f8f1ac997d290af22d5a5cdbd5f04cbf0fa18d5388871e980a4fc67875037821799b356032f851732dee1017b2ee7de84f5c2a2b8bfd5604f5 - languageName: node - linkType: hard - "@standard-schema/spec@npm:^1.1.0": version: 1.1.0 resolution: "@standard-schema/spec@npm:1.1.0" @@ -9061,40 +9051,12 @@ __metadata: "@ts-morph/common@npm:~0.25.0": version: 0.25.0 - resolution: "@ts-morph/common@npm:0.25.0" - dependencies: - minimatch: "npm:^9.0.4" - path-browserify: "npm:^1.0.1" - tinyglobby: "npm:^0.2.9" - checksum: 10/9b192dfebb7179e96267ea1720f6afe77964f9f89ecaca41d372da206cdfd838a77c1080026c73115a5d3a88752b1504ec7380907e26b6424e8fb7f7625c7461 - languageName: node - linkType: hard - -"@tsconfig/node10@npm:^1.0.7": - version: 1.0.11 - resolution: "@tsconfig/node10@npm:1.0.11" - checksum: 10/51fe47d55fe1b80ec35e6e5ed30a13665fd3a531945350aa74a14a1e82875fb60b350c2f2a5e72a64831b1b6bc02acb6760c30b3738b54954ec2dea82db7a267 - languageName: node - linkType: hard - -"@tsconfig/node12@npm:^1.0.7": - version: 1.0.11 - resolution: "@tsconfig/node12@npm:1.0.11" - checksum: 10/5ce29a41b13e7897a58b8e2df11269c5395999e588b9a467386f99d1d26f6c77d1af2719e407621412520ea30517d718d5192a32403b8dfcc163bf33e40a338a - languageName: node - linkType: hard - -"@tsconfig/node14@npm:^1.0.0": - version: 1.0.3 - resolution: "@tsconfig/node14@npm:1.0.3" - checksum: 10/19275fe80c4c8d0ad0abed6a96dbf00642e88b220b090418609c4376e1cef81bf16237bf170ad1b341452feddb8115d8dd2e5acdfdea1b27422071163dc9ba9d - languageName: node - linkType: hard - -"@tsconfig/node16@npm:^1.0.2": - version: 1.0.4 - resolution: "@tsconfig/node16@npm:1.0.4" - checksum: 10/202319785901f942a6e1e476b872d421baec20cf09f4b266a1854060efbf78cde16a4d256e8bc949d31e6cd9a90f1e8ef8fb06af96a65e98338a2b6b0de0a0ff + resolution: "@ts-morph/common@npm:0.25.0" + dependencies: + minimatch: "npm:^9.0.4" + path-browserify: "npm:^1.0.1" + tinyglobby: "npm:^0.2.9" + checksum: 10/9b192dfebb7179e96267ea1720f6afe77964f9f89ecaca41d372da206cdfd838a77c1080026c73115a5d3a88752b1504ec7380907e26b6424e8fb7f7625c7461 languageName: node linkType: hard @@ -9563,12 +9525,12 @@ __metadata: linkType: hard "@types/markdown-it@npm:^14.1.1": - version: 14.1.2 - resolution: "@types/markdown-it@npm:14.1.2" + version: 14.2.0 + resolution: "@types/markdown-it@npm:14.2.0" dependencies: "@types/linkify-it": "npm:^5" "@types/mdurl": "npm:^2" - checksum: 10/ca2f239c8d59610b9f936fd40261a6ccf2fa1ae27a21816c031e5712542dcf9ee01e2fe29b31118df90716e11ade54e47d92a498e9b6488800e77ca8827255a2 + checksum: 10/9376d449bd103c74853abf8abdc649354aca59c628fe431c75cae1c916dcef6a308ff4c30eb5f8477a6df875ecc8229d1ff5316652ce7c7070baf46d91e2b43e languageName: node linkType: hard @@ -9643,7 +9605,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^18.11.18, @types/node@npm:^18.11.9": +"@types/node@npm:^18.11.18": version: 18.19.68 resolution: "@types/node@npm:18.19.68" dependencies: @@ -9697,11 +9659,11 @@ __metadata: linkType: hard "@types/react-dom@npm:^18": - version: 18.3.7 - resolution: "@types/react-dom@npm:18.3.7" - peerDependencies: - "@types/react": ^18.0.0 - checksum: 10/317569219366d487a3103ba1e5e47154e95a002915fdcf73a44162c48fe49c3a57fcf7f57fc6979e70d447112681e6b13c6c3c1df289db8b544df4aab2d318f3 + version: 18.3.1 + resolution: "@types/react-dom@npm:18.3.1" + dependencies: + "@types/react": "npm:*" + checksum: 10/33f9ba79b26641ddf00a8699c30066b7e3573ab254e97475bf08f82fab83a6d3ce8d4ebad86afeb49bb8df3374390a9ba93125cece33badc4b3e8f7eac3c84d8 languageName: node linkType: hard @@ -9778,13 +9740,6 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:7.5.8": - version: 7.5.8 - resolution: "@types/semver@npm:7.5.8" - checksum: 10/3496808818ddb36deabfe4974fd343a78101fa242c4690044ccdc3b95dcf8785b494f5d628f2f47f38a702f8db9c53c67f47d7818f2be1b79f2efb09692e1178 - languageName: node - linkType: hard - "@types/send@npm:*, @types/send@npm:<1": version: 0.17.6 resolution: "@types/send@npm:0.17.6" @@ -10569,16 +10524,7 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.1.1, acorn-walk@npm:^8.3.4": - version: 8.3.4 - resolution: "acorn-walk@npm:8.3.4" - dependencies: - acorn: "npm:^8.11.0" - checksum: 10/871386764e1451c637bb8ab9f76f4995d408057e9909be6fb5ad68537ae3375d85e6a6f170b98989f44ab3ff6c74ad120bc2779a3d577606e7a0cd2b4efcaf77 - languageName: node - linkType: hard - -"acorn@npm:^8.11.0, acorn@npm:^8.14.1, acorn@npm:^8.15.0, acorn@npm:^8.16.0, acorn@npm:^8.4.1, acorn@npm:^8.9.0": +"acorn@npm:^8.15.0, acorn@npm:^8.16.0, acorn@npm:^8.9.0": version: 8.16.0 resolution: "acorn@npm:8.16.0" bin: @@ -10594,10 +10540,10 @@ __metadata: languageName: node linkType: hard -"adm-zip@npm:0.5.10": - version: 0.5.10 - resolution: "adm-zip@npm:0.5.10" - checksum: 10/c5ab79b77114d8277f0cbfd6cca830198d6c7ee4971f6960f48e08cd2375953b11dc71729b7f396abd51d2d6cce8c862fad185ea90cb2c84ab5161c37ed1b099 +"adm-zip@npm:0.6.0": + version: 0.6.0 + resolution: "adm-zip@npm:0.6.0" + checksum: 10/6a49ab39055d8f716958d3a2055491ab86fe3359c4afeed9b14bbce78fb847bcd3cbaa0ff4921aaa19824aaa29d06691def1f53263db5224d06c068f922c5ba2 languageName: node linkType: hard @@ -10748,7 +10694,7 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:4.1.3, ansi-colors@npm:^4.1.1, ansi-colors@npm:^4.1.3": +"ansi-colors@npm:^4.1.1, ansi-colors@npm:^4.1.3": version: 4.1.3 resolution: "ansi-colors@npm:4.1.3" checksum: 10/43d6e2fc7b1c6e4dc373de708ee76311ec2e0433e7e8bd3194e7ff123ea6a747428fc61afdcf5969da5be3a5f0fd054602bec56fc0ebe249ce2fcde6e649e3c2 @@ -10835,13 +10781,6 @@ __metadata: languageName: node linkType: hard -"ansis@npm:^4.2.0": - version: 4.2.0 - resolution: "ansis@npm:4.2.0" - checksum: 10/493e15fad267bd6e3e275d6886c3b3c96a075784d9eae3e16d16383d488e94cc3deb1b357e1246f572599767360548ef9e5b7eab9b72e4ee3f7bad9ce6bc8797 - languageName: node - linkType: hard - "any-promise@npm:^1.0.0": version: 1.3.0 resolution: "any-promise@npm:1.3.0" @@ -10859,13 +10798,6 @@ __metadata: languageName: node linkType: hard -"app-root-path@npm:^3.1.0": - version: 3.1.0 - resolution: "app-root-path@npm:3.1.0" - checksum: 10/b4cdab5f7e51ec43fa04c97eca2adedf8e18d6c3dd21cd775b70457c5e71f0441c692a49dcceb426f192640b7393dcd41d85c36ef98ecb7c785a53159c912def - languageName: node - linkType: hard - "append-field@npm:^1.0.0": version: 1.0.0 resolution: "append-field@npm:1.0.0" @@ -10927,13 +10859,6 @@ __metadata: languageName: node linkType: hard -"arg@npm:^4.1.0": - version: 4.1.3 - resolution: "arg@npm:4.1.3" - checksum: 10/969b491082f20cad166649fa4d2073ea9e974a4e5ac36247ca23d2e5a8b3cb12d60e9ff70a8acfe26d76566c71fd351ee5e6a9a6595157eb36f92b1fd64e1599 - languageName: node - linkType: hard - "argparse@npm:^1.0.7, argparse@npm:~1.0.9": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -11292,14 +11217,14 @@ __metadata: linkType: hard "axios@npm:^1.16.0": - version: 1.18.1 - resolution: "axios@npm:1.18.1" + version: 1.19.0 + resolution: "axios@npm:1.19.0" dependencies: follow-redirects: "npm:^1.16.0" - form-data: "npm:^4.0.5" + form-data: "npm:^4.0.6" https-proxy-agent: "npm:^5.0.1" proxy-from-env: "npm:^2.1.0" - checksum: 10/c4cdced3ee0a9bf7dcae189fbc74a124aa079d4f04dbd995e602d39c1419476e8d021f87ee39ac8d8398e5a55e6d0b986238b3645f0d9177ac80de87c2a73f18 + checksum: 10/d27e263b003f2dc1e6d0e2ab062dbc7c2b15668c25723e1e2072f139505c1fbd1f8cffef77d5ec05bb8e3cefa6fc5325c6446e747669fd95d85519529fa0db0b languageName: node linkType: hard @@ -12156,7 +12081,7 @@ __metadata: languageName: node linkType: hard -"casbin@npm:^5.27.0, casbin@npm:^5.27.1": +"casbin@npm:^5.27.1": version: 5.34.0 resolution: "casbin@npm:5.34.0" dependencies: @@ -13151,22 +13076,13 @@ __metadata: languageName: node linkType: hard -"create-require@npm:^1.1.0, create-require@npm:^1.1.1": +"create-require@npm:^1.1.1": version: 1.1.1 resolution: "create-require@npm:1.1.1" checksum: 10/a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff languageName: node linkType: hard -"cron-parser@npm:^4.2.0": - version: 4.9.0 - resolution: "cron-parser@npm:4.9.0" - dependencies: - luxon: "npm:^3.2.1" - checksum: 10/ffca5e532a5ee0923412ee6e4c7f9bbceacc6ddf8810c16d3e9fb4fe5ec7e2de1b6896d7956f304bb6bc96b0ce37ad7e3935304179d52951c18d84107184faa7 - languageName: node - linkType: hard - "cron@npm:^3.0.0": version: 3.1.9 resolution: "cron@npm:3.1.9" @@ -13656,13 +13572,6 @@ __metadata: languageName: node linkType: hard -"dayjs@npm:^1.11.20": - version: 1.11.21 - resolution: "dayjs@npm:1.11.21" - checksum: 10/dd16f9f2706b61b90e3c346a3211ac3afd9e26193136ce0e87f70bf74d1c17a447bceb230a88b175467fc9f5e3243d8c1544b9897092a12d9c80ee44d338dcb6 - languageName: node - linkType: hard - "debounce@npm:^1.2.0": version: 1.2.1 resolution: "debounce@npm:1.2.1" @@ -13737,7 +13646,7 @@ __metadata: languageName: node linkType: hard -"dedent@npm:^1.6.0, dedent@npm:^1.7.2": +"dedent@npm:^1.6.0": version: 1.7.2 resolution: "dedent@npm:1.7.2" peerDependencies: @@ -13983,13 +13892,6 @@ __metadata: languageName: node linkType: hard -"diff@npm:^4.0.1": - version: 4.0.2 - resolution: "diff@npm:4.0.2" - checksum: 10/ec09ec2101934ca5966355a229d77afcad5911c92e2a77413efda5455636c4cf2ce84057e2d7715227a2eeeda04255b849bd3ae3a4dd22eb22e86e76456df069 - languageName: node - linkType: hard - "diff@npm:^5.0.0": version: 5.2.0 resolution: "diff@npm:5.2.0" @@ -14175,13 +14077,6 @@ __metadata: languageName: node linkType: hard -"dotenv@npm:^16.6.1": - version: 16.6.1 - resolution: "dotenv@npm:16.6.1" - checksum: 10/1d1897144344447ffe62aa1a6d664f4cd2e0784e0aff787eeeec1940ded32f8e4b5b506d665134fc87157baa086fce07ec6383970a2b6d2e7985beaed6a4cc14 - languageName: node - linkType: hard - "dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1": version: 1.0.1 resolution: "dunder-proto@npm:1.0.1" @@ -15256,15 +15151,6 @@ __metadata: languageName: node linkType: hard -"expand-tilde@npm:^2.0.0, expand-tilde@npm:^2.0.2": - version: 2.0.2 - resolution: "expand-tilde@npm:2.0.2" - dependencies: - homedir-polyfill: "npm:^1.0.1" - checksum: 10/2efe6ed407d229981b1b6ceb552438fbc9e5c7d6a6751ad6ced3e0aa5cf12f0b299da695e90d6c2ac79191b5c53c613e508f7149e4573abfbb540698ddb7301a - languageName: node - linkType: hard - "expect@npm:30.3.0, expect@npm:^30.0.0": version: 30.3.0 resolution: "expect@npm:30.3.0" @@ -15673,24 +15559,6 @@ __metadata: languageName: node linkType: hard -"find-file-up@npm:^2.0.1": - version: 2.0.1 - resolution: "find-file-up@npm:2.0.1" - dependencies: - resolve-dir: "npm:^1.0.1" - checksum: 10/dfe820bfb80e75bed5dd5080057858c0ad2393e1438c48a3bb682663e9ecdcfbe3224ed4768bfedd00776800b4ae76bc8953d250d15ae3feabf381d2c6d04268 - languageName: node - linkType: hard - -"find-pkg@npm:2.0.0": - version: 2.0.0 - resolution: "find-pkg@npm:2.0.0" - dependencies: - find-file-up: "npm:^2.0.1" - checksum: 10/44785204c8bbbdfeaece6b834ba81a35163421c30e20f531281d26e6b5890663d7ac884b82a9aebf6ce23e479336cd6f70ea5597da35495c16abdeba2fd4f845 - languageName: node - linkType: hard - "find-root@npm:^1.1.0": version: 1.1.0 resolution: "find-root@npm:1.1.0" @@ -15849,16 +15717,16 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0, form-data@npm:^4.0.5": - version: 4.0.5 - resolution: "form-data@npm:4.0.5" +"form-data@npm:^4.0.0, form-data@npm:^4.0.5, form-data@npm:^4.0.6": + version: 4.0.6 + resolution: "form-data@npm:4.0.6" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" es-set-tostringtag: "npm:^2.1.0" - hasown: "npm:^2.0.2" - mime-types: "npm:^2.1.12" - checksum: 10/52ecd6e927c8c4e215e68a7ad5e0f7c1031397439672fd9741654b4a94722c4182e74cc815b225dcb5be3f4180f36428f67c6dd39eaa98af0dcfdd26c00c19cd + hasown: "npm:^2.0.4" + mime-types: "npm:^2.1.35" + checksum: 10/de6614c8537c92fa5fa3ee7e827758f98f5a9c033f348b7de81855ef36e5cb867e75d9f405d9483ab8d724a4a20d4e79926a299fa8dbba38f530eb659f0884e4 languageName: node linkType: hard @@ -16382,7 +16250,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^13.0.0": +"glob@npm:^13.0.0, glob@npm:^13.0.6": version: 13.0.6 resolution: "glob@npm:13.0.6" dependencies: @@ -16393,7 +16261,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": +"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -16434,17 +16302,6 @@ __metadata: languageName: node linkType: hard -"global-modules@npm:^1.0.0": - version: 1.0.0 - resolution: "global-modules@npm:1.0.0" - dependencies: - global-prefix: "npm:^1.0.1" - is-windows: "npm:^1.0.1" - resolve-dir: "npm:^1.0.0" - checksum: 10/e4031a01c0c7401349bb69e1499c7268d636552b16374c0002d677c7a6185da6782a2927a7a3a7c046eb7be97cd26b3c7b1b736f9818ecc7ac09e9d61449065e - languageName: node - linkType: hard - "global-modules@npm:^2.0.0": version: 2.0.0 resolution: "global-modules@npm:2.0.0" @@ -16454,19 +16311,6 @@ __metadata: languageName: node linkType: hard -"global-prefix@npm:^1.0.1": - version: 1.0.2 - resolution: "global-prefix@npm:1.0.2" - dependencies: - expand-tilde: "npm:^2.0.2" - homedir-polyfill: "npm:^1.0.1" - ini: "npm:^1.3.4" - is-windows: "npm:^1.0.1" - which: "npm:^1.2.14" - checksum: 10/68cf78f81cd85310095ca1f0ec22dd5f43a1059646b2c7b3fc4a7c9ce744356e66ca833adda4e5753e38021847aaec393a159a029ba2d257c08ccb3f00ca2899 - languageName: node - linkType: hard - "global-prefix@npm:^3.0.0": version: 3.0.0 resolution: "global-prefix@npm:3.0.0" @@ -16755,12 +16599,12 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.0, hasown@npm:^2.0.2": - version: 2.0.2 - resolution: "hasown@npm:2.0.2" +"hasown@npm:^2.0.0, hasown@npm:^2.0.2, hasown@npm:^2.0.4": + version: 2.0.4 + resolution: "hasown@npm:2.0.4" dependencies: function-bind: "npm:^1.1.2" - checksum: 10/7898a9c1788b2862cf0f9c345a6bec77ba4a0c0983c7f19d610c382343d4f98fa260686b225dfb1f88393a66679d2ec58ee310c1d6868c081eda7918f32cc70a + checksum: 10/13823863ae48161068b4c51606a3128451c66f14545a5169d667fe9fca168dcd38c27570c7a299e32ef844b8da3d55def7fe88602f8970d4311fb543ee88001a languageName: node linkType: hard @@ -16936,15 +16780,6 @@ __metadata: languageName: node linkType: hard -"homedir-polyfill@npm:^1.0.1": - version: 1.0.3 - resolution: "homedir-polyfill@npm:1.0.3" - dependencies: - parse-passwd: "npm:^1.0.0" - checksum: 10/18dd4db87052c6a2179d1813adea0c4bfcfa4f9996f0e226fefb29eb3d548e564350fa28ec46b0bf1fbc0a1d2d6922ceceb80093115ea45ff8842a4990139250 - languageName: node - linkType: hard - "hoopy@npm:^0.1.4": version: 0.1.4 resolution: "hoopy@npm:0.1.4" @@ -17413,7 +17248,7 @@ __metadata: languageName: node linkType: hard -"ini@npm:^1.3.4, ini@npm:^1.3.5, ini@npm:~1.3.0": +"ini@npm:^1.3.5, ini@npm:~1.3.0": version: 1.3.8 resolution: "ini@npm:1.3.8" checksum: 10/314ae176e8d4deb3def56106da8002b462221c174ddb7ce0c49ee72c8cd1f9044f7b10cc555a7d8850982c3b9ca96fc212122749f5234bc2b6fb05fb942ed566 @@ -18013,7 +17848,7 @@ __metadata: languageName: node linkType: hard -"is-windows@npm:^1.0.0, is-windows@npm:^1.0.1": +"is-windows@npm:^1.0.0": version: 1.0.2 resolution: "is-windows@npm:1.0.2" checksum: 10/438b7e52656fe3b9b293b180defb4e448088e7023a523ec21a91a80b9ff8cdb3377ddb5b6e60f7c7de4fa8b63ab56e121b6705fe081b3cf1b828b0a380009ad7 @@ -18734,18 +18569,29 @@ __metadata: linkType: hard "js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.1, js-yaml@npm:^3.6.1, js-yaml@npm:^3.8.3": - version: 3.15.0 - resolution: "js-yaml@npm:3.15.0" + version: 3.14.2 + resolution: "js-yaml@npm:3.14.2" dependencies: argparse: "npm:^1.0.7" esprima: "npm:^4.0.0" bin: js-yaml: bin/js-yaml.js - checksum: 10/2fdf3a1453ed93a8e06d6ca8054c0bec145cf40ab51f305d1071736a03668b95e40f47cfd0239d7d50019b4780a18cdaca3c935def935594c9876964c49f1185 + checksum: 10/172e0b6007b0bf0fc8d2469c94424f7dd765c64a047d2b790831fecef2204a4054eabf4d911eb73ab8c9a3256ab8ba1ee8d655b789bf24bf059c772acc2075a1 + languageName: node + linkType: hard + +"js-yaml@npm:^4.1.0, js-yaml@npm:^4.2.0": + version: 4.3.1 + resolution: "js-yaml@npm:4.3.1" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10/2ce71b5d632abbd77da80447bf860e8a0264e54bffe94840984887d58b023761495b523727547904517a6107a1ef189854b361e0fc44995ee13a84f222d7bd42 languageName: node linkType: hard -"js-yaml@npm:^4.1.0, js-yaml@npm:~4.1.0": +"js-yaml@npm:~4.1.0": version: 4.1.1 resolution: "js-yaml@npm:4.1.1" dependencies: @@ -19427,12 +19273,12 @@ __metadata: languageName: node linkType: hard -"linkify-it@npm:^5.0.1": - version: 5.0.1 - resolution: "linkify-it@npm:5.0.1" +"linkify-it@npm:^5.0.0": + version: 5.0.0 + resolution: "linkify-it@npm:5.0.0" dependencies: uc.micro: "npm:^2.0.0" - checksum: 10/47933d11cda495fd31e3bb3c086358305362f4413ffa289500d1e48164acb221b3be6bde6404b86dba93b7fe701c95f318bee285f7613092cd7f28a9e97261e7 + checksum: 10/ef3b7609dda6ec0c0be8a7b879cea195f0d36387b0011660cd6711bba0ad82137f59b458b7e703ec74f11d88e7c1328e2ad9b855a8500c0ded67461a8c4519e6 languageName: node linkType: hard @@ -19729,13 +19575,6 @@ __metadata: languageName: node linkType: hard -"long-timeout@npm:0.1.1": - version: 0.1.1 - resolution: "long-timeout@npm:0.1.1" - checksum: 10/48668e5362cb74c4b77a6b833d59f149b9bb9e99c5a5097609807e2597cd0920613b2a42b89bd0870848298be3691064d95599a04ae010023d07dba39932afa7 - languageName: node - linkType: hard - "long@npm:^5.0.0, long@npm:^5.2.1": version: 5.2.3 resolution: "long@npm:5.2.3" @@ -19843,7 +19682,7 @@ __metadata: languageName: node linkType: hard -"luxon@npm:^3.0.0, luxon@npm:^3.2.1, luxon@npm:~3.5.0": +"luxon@npm:^3.0.0, luxon@npm:~3.5.0": version: 3.5.0 resolution: "luxon@npm:3.5.0" checksum: 10/48f86e6c1c96815139f8559456a3354a276ba79bcef0ae0d4f2172f7652f3ba2be2237b0e103b8ea0b79b47715354ac9fac04eb1db3485dcc72d5110491dd47f @@ -19877,13 +19716,6 @@ __metadata: languageName: node linkType: hard -"make-error@npm:^1.1.1": - version: 1.3.6 - resolution: "make-error@npm:1.3.6" - checksum: 10/b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 - languageName: node - linkType: hard - "make-fetch-happen@npm:^10.0.3": version: 10.2.1 resolution: "make-fetch-happen@npm:10.2.1" @@ -19962,18 +19794,18 @@ __metadata: linkType: hard "markdown-it@npm:^14.1.0": - version: 14.2.0 - resolution: "markdown-it@npm:14.2.0" + version: 14.1.0 + resolution: "markdown-it@npm:14.1.0" dependencies: argparse: "npm:^2.0.1" entities: "npm:^4.4.0" - linkify-it: "npm:^5.0.1" + linkify-it: "npm:^5.0.0" mdurl: "npm:^2.0.0" punycode.js: "npm:^2.3.1" uc.micro: "npm:^2.1.0" bin: markdown-it: bin/markdown-it.mjs - checksum: 10/f5cdb7ca9c8115114137201590b2697ee6ca69d2d4701a0313696629d5de4022525eedcad31800263c70f04dfba0a8eae835b6aa84b65e94ae32d63d04e66211 + checksum: 10/f34f921be178ed0607ba9e3e27c733642be445e9bb6b1dba88da7aafe8ba1bc5d2f1c3aa8f3fc33b49a902da4e4c08c2feadfafb290b8c7dda766208bb6483a9 languageName: node linkType: hard @@ -20712,7 +20544,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:^2.1.35, mime-types@npm:~2.1.17, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:^2.1.35, mime-types@npm:~2.1.17, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -21275,12 +21107,12 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.16": - version: 3.3.16 - resolution: "nanoid@npm:3.3.16" +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" bin: nanoid: bin/nanoid.cjs - checksum: 10/8004af92b5541af1dbd23b69845b5026f777d5b7ef07163cea1837aae86e052ced8b383cecbf8a4f1b5e77ae207df96dc45e16b9e0fa3c4b761d085f1e42851b + checksum: 10/ac1eb60f615b272bccb0e2b9cd933720dad30bf9708424f691b8113826bb91aca7e9d14ef5d9415a6ba15c266b37817256f58d8ce980c82b0ba3185352565679 languageName: node linkType: hard @@ -21508,17 +21340,6 @@ __metadata: languageName: node linkType: hard -"node-schedule@npm:2.1.1": - version: 2.1.1 - resolution: "node-schedule@npm:2.1.1" - dependencies: - cron-parser: "npm:^4.2.0" - long-timeout: "npm:0.1.1" - sorted-array-functions: "npm:^1.3.0" - checksum: 10/0b0449f8a1f784cd599a8d79b1fa404ed9e3e4e2b1a48f027c97fd0632cd86e48ad762d366d6b6f9d48a940cad5b7afbdb1b833649ee870407591a6cf1297749 - languageName: node - linkType: hard - "node-stdlib-browser@npm:^1.3.1": version: 1.3.1 resolution: "node-stdlib-browser@npm:1.3.1" @@ -21770,9 +21591,9 @@ __metadata: linkType: hard "oidc-token-hash@npm:^5.0.3": - version: 5.2.0 - resolution: "oidc-token-hash@npm:5.2.0" - checksum: 10/4eb991b454722d051ac79c98ab08c5ec9e40ae98835797690f1419dd396d680dbbdb17dfb700c4a1b6be472b795fb9b494c15c6af9b026d576f3c9482b43c03f + version: 5.0.3 + resolution: "oidc-token-hash@npm:5.0.3" + checksum: 10/35fa19aea9ff2c509029ec569d74b778c8a215b92bd5e6e9bc4ebbd7ab035f44304ff02430a6397c3fb7c1d15ebfa467807ca0bcd31d06ba610b47798287d303 languageName: node linkType: hard @@ -22226,13 +22047,6 @@ __metadata: languageName: node linkType: hard -"parse-passwd@npm:^1.0.0": - version: 1.0.0 - resolution: "parse-passwd@npm:1.0.0" - checksum: 10/4e55e0231d58f828a41d0f1da2bf2ff7bcef8f4cb6146e69d16ce499190de58b06199e6bd9b17fbf0d4d8aef9052099cdf8c4f13a6294b1a522e8e958073066e - languageName: node - linkType: hard - "parse-path@npm:^7.0.0": version: 7.0.0 resolution: "parse-path@npm:7.0.0" @@ -22347,13 +22161,6 @@ __metadata: languageName: node linkType: hard -"path-equal@npm:^1.2.5": - version: 1.2.5 - resolution: "path-equal@npm:1.2.5" - checksum: 10/fa4ef398dea6bd7bf36c5fe62b5f5c2c14fe1f1340cf355eb8a40c86577318dfa0401df86464bb0cc33ed227f115b2afec10d1adaa64260dedbbc23d33f3abbb - languageName: node - linkType: hard - "path-exists@npm:^3.0.0": version: 3.0.0 resolution: "path-exists@npm:3.0.0" @@ -23165,13 +22972,13 @@ __metadata: linkType: hard "postcss@npm:^8.1.0, postcss@npm:^8.4.33": - version: 8.5.22 - resolution: "postcss@npm:8.5.22" + version: 8.4.47 + resolution: "postcss@npm:8.4.47" dependencies: - nanoid: "npm:^3.3.16" - picocolors: "npm:^1.1.1" + nanoid: "npm:^3.3.7" + picocolors: "npm:^1.1.0" source-map-js: "npm:^1.2.1" - checksum: 10/7944444f267f2d94c7caeed66f3da56d5b947bd4a7e57a166a5161af25c6006dd73f40e2a1a6822f3d7fccc2fa005778c03058368eb7efca1b5038aec55abd14 + checksum: 10/f2b50ba9b6fcb795232b6bb20de7cdc538c0025989a8ed9c4438d1960196ba3b7eaff41fdb1a5c701b3504651ea87aeb685577707f0ae4d6ce6f3eae5df79a81 languageName: node linkType: hard @@ -24308,13 +24115,6 @@ __metadata: languageName: node linkType: hard -"reflect-metadata@npm:^0.1.13": - version: 0.1.14 - resolution: "reflect-metadata@npm:0.1.14" - checksum: 10/fcab9c17ec3b9fea0e2f748c2129aceb57c24af6d8d13842b8a77c8c79dde727d7456ce293e76e8d7b267d1dbf93eea4c5b3c9101299a789a075824f2e40f1ee - languageName: node - linkType: hard - "reflect-metadata@npm:^0.2.2": version: 0.2.2 resolution: "reflect-metadata@npm:0.2.2" @@ -24513,16 +24313,6 @@ __metadata: languageName: node linkType: hard -"resolve-dir@npm:^1.0.0, resolve-dir@npm:^1.0.1": - version: 1.0.1 - resolution: "resolve-dir@npm:1.0.1" - dependencies: - expand-tilde: "npm:^2.0.0" - global-modules: "npm:^1.0.0" - checksum: 10/ef736b8ed60d6645c3b573da17d329bfb50ec4e1d6c5ffd6df49e3497acef9226f9810ea6823b8ece1560e01dcb13f77a9f6180d4f242d00cc9a8f4de909c65c - languageName: node - linkType: hard - "resolve-from@npm:^4.0.0": version: 4.0.0 resolution: "resolve-from@npm:4.0.0" @@ -24544,19 +24334,6 @@ __metadata: languageName: node linkType: hard -"resolve@npm:1.22.8": - version: 1.22.8 - resolution: "resolve@npm:1.22.8" - dependencies: - is-core-module: "npm:^2.13.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10/c473506ee01eb45cbcfefb68652ae5759e092e6b0fb64547feadf9736a6394f258fbc6f88e00c5ca36d5477fbb65388b272432a3600fa223062e54333c156753 - languageName: node - linkType: hard - "resolve@npm:^1.1.7, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:~1.22.1, resolve@npm:~1.22.2": version: 1.22.11 resolution: "resolve@npm:1.22.11" @@ -24583,19 +24360,6 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A1.22.8#optional!builtin": - version: 1.22.8 - resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" - dependencies: - is-core-module: "npm:^2.13.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10/f345cd37f56a2c0275e3fe062517c650bb673815d885e7507566df589375d165bbbf4bdb6aa95600a9bc55f4744b81f452b5a63f95b9f10a72787dba3c90890a - languageName: node - linkType: hard - "resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.17.0#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin, resolve@patch:resolve@npm%3A~1.22.2#optional!builtin": version: 1.22.11 resolution: "resolve@patch:resolve@npm%3A1.22.11#optional!builtin::version=1.22.11&hash=c3c19d" @@ -25000,7 +24764,7 @@ __metadata: languageName: node linkType: hard -"safe-stable-stringify@npm:^2.2.0, safe-stable-stringify@npm:^2.3.1": +"safe-stable-stringify@npm:^2.3.1, safe-stable-stringify@npm:^2.5.0": version: 2.5.0 resolution: "safe-stable-stringify@npm:2.5.0" checksum: 10/2697fa186c17c38c3ca5309637b4ac6de2f1c3d282da27cd5e1e3c88eca0fb1f9aea568a6aabdf284111592c8782b94ee07176f17126031be72ab1313ed46c5c @@ -25119,15 +24883,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.6.3": - version: 7.6.3 - resolution: "semver@npm:7.6.3" - bin: - semver: bin/semver.js - checksum: 10/36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 - languageName: node - linkType: hard - "semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" @@ -25309,7 +25064,7 @@ __metadata: languageName: node linkType: hard -"sha.js@npm:^2.4.0, sha.js@npm:^2.4.12, sha.js@npm:^2.4.8": +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8": version: 2.4.12 resolution: "sha.js@npm:2.4.12" dependencies: @@ -25539,13 +25294,6 @@ __metadata: languageName: node linkType: hard -"sorted-array-functions@npm:^1.3.0": - version: 1.3.0 - resolution: "sorted-array-functions@npm:1.3.0" - checksum: 10/673fd39ca3b6c92644d4483eac1700bb7d7555713a536822a7522a35af559bef3e72f10d89356b75042dc394cd7c2e2ab6f40024385218ec3c85bb7335032857 - languageName: node - linkType: hard - "source-list-map@npm:^2.0.0": version: 2.0.1 resolution: "source-list-map@npm:2.0.1" @@ -25694,13 +25442,6 @@ __metadata: languageName: node linkType: hard -"sql-highlight@npm:^6.1.0": - version: 6.1.0 - resolution: "sql-highlight@npm:6.1.0" - checksum: 10/6cd92e7ca3046563f3daf2086adc4c2e1ce43784e59827a12bb9e569bf915eace1d800713f4d2798fc7d475f64852bf08001dca8dd409e9895ba5e0e170b94ff - languageName: node - linkType: hard - "sqlstring@npm:^2.3.2": version: 2.3.3 resolution: "sqlstring@npm:2.3.3" @@ -26970,6 +26711,24 @@ __metadata: languageName: node linkType: hard +"ts-json-schema-generator@npm:^2.9.0": + version: 2.9.0 + resolution: "ts-json-schema-generator@npm:2.9.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + commander: "npm:^14.0.3" + glob: "npm:^13.0.6" + json5: "npm:^2.2.3" + normalize-path: "npm:^3.0.0" + safe-stable-stringify: "npm:^2.5.0" + tslib: "npm:^2.8.1" + typescript: "npm:^5.9.3" + bin: + ts-json-schema-generator: bin/ts-json-schema-generator.js + checksum: 10/50a18cb6a1171e495af16a4fe8165000d0155ef693cec9345bacb8a5b6cc559dcc730443ebdc025590cd29ffd2019564267a8222c7f6389bdf5558856fbe479b + languageName: node + linkType: hard + "ts-morph@npm:^24.0.0": version: 24.0.0 resolution: "ts-morph@npm:24.0.0" @@ -26980,44 +26739,6 @@ __metadata: languageName: node linkType: hard -"ts-node@npm:^10.9.1": - version: 10.9.2 - resolution: "ts-node@npm:10.9.2" - dependencies: - "@cspotcode/source-map-support": "npm:^0.8.0" - "@tsconfig/node10": "npm:^1.0.7" - "@tsconfig/node12": "npm:^1.0.7" - "@tsconfig/node14": "npm:^1.0.0" - "@tsconfig/node16": "npm:^1.0.2" - acorn: "npm:^8.4.1" - acorn-walk: "npm:^8.1.1" - arg: "npm:^4.1.0" - create-require: "npm:^1.1.0" - diff: "npm:^4.0.1" - make-error: "npm:^1.1.1" - v8-compile-cache-lib: "npm:^3.0.1" - yn: "npm:3.1.1" - peerDependencies: - "@swc/core": ">=1.2.50" - "@swc/wasm": ">=1.2.50" - "@types/node": "*" - typescript: ">=2.7" - peerDependenciesMeta: - "@swc/core": - optional: true - "@swc/wasm": - optional: true - bin: - ts-node: dist/bin.js - ts-node-cwd: dist/bin-cwd.js - ts-node-esm: dist/bin-esm.js - ts-node-script: dist/bin-script.js - ts-node-transpile-only: dist/bin-transpile.js - ts-script: dist/bin-script-deprecated.js - checksum: 10/a91a15b3c9f76ac462f006fa88b6bfa528130dcfb849dd7ef7f9d640832ab681e235b8a2bc58ecde42f72851cc1d5d4e22c901b0c11aa51001ea1d395074b794 - languageName: node - linkType: hard - "tsconfig-paths@npm:^3.15.0": version: 3.15.0 resolution: "tsconfig-paths@npm:3.15.0" @@ -27218,113 +26939,6 @@ __metadata: languageName: node linkType: hard -"typeorm-adapter@npm:^1.6.1": - version: 1.7.0 - resolution: "typeorm-adapter@npm:1.7.0" - dependencies: - casbin: "npm:^5.27.0" - reflect-metadata: "npm:^0.1.13" - typeorm: "npm:^0.3.17" - checksum: 10/4635c703033a555a804b829dc8e2741f39edbc4ddf0ea094510c1a68714e1c16d3ab0bf9f2b5ee10f8d33354f90e9dbaffba7b9e809e781fa452ad46eab0a2fa - languageName: node - linkType: hard - -"typeorm@npm:^0.3.17": - version: 0.3.30 - resolution: "typeorm@npm:0.3.30" - dependencies: - "@sqltools/formatter": "npm:^1.2.5" - ansis: "npm:^4.2.0" - app-root-path: "npm:^3.1.0" - buffer: "npm:^6.0.3" - dayjs: "npm:^1.11.20" - debug: "npm:^4.4.3" - dedent: "npm:^1.7.2" - dotenv: "npm:^16.6.1" - glob: "npm:^10.5.0" - reflect-metadata: "npm:^0.2.2" - sha.js: "npm:^2.4.12" - sql-highlight: "npm:^6.1.0" - tslib: "npm:^2.8.1" - uuid: "npm:^11.1.1" - yargs: "npm:^17.7.2" - peerDependencies: - "@google-cloud/spanner": ^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - "@sap/hana-client": ^2.14.22 - better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 - ioredis: ^5.0.4 - mongodb: ^5.8.0 || ^6.0.0 - mssql: ^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0 - mysql2: ^2.2.5 || ^3.0.1 - oracledb: ^6.3.0 - pg: ^8.5.1 - pg-native: ^3.0.0 - pg-query-stream: ^4.0.0 - redis: ^3.1.1 || ^4.0.0 || ^5.0.14 - sql.js: ^1.4.0 - sqlite3: ^5.0.3 - ts-node: ^10.7.0 - typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 - peerDependenciesMeta: - "@google-cloud/spanner": - optional: true - "@sap/hana-client": - optional: true - better-sqlite3: - optional: true - ioredis: - optional: true - mongodb: - optional: true - mssql: - optional: true - mysql2: - optional: true - oracledb: - optional: true - pg: - optional: true - pg-native: - optional: true - pg-query-stream: - optional: true - redis: - optional: true - sql.js: - optional: true - sqlite3: - optional: true - ts-node: - optional: true - typeorm-aurora-data-api-driver: - optional: true - bin: - typeorm: cli.js - typeorm-ts-node-commonjs: cli-ts-node-commonjs.js - typeorm-ts-node-esm: cli-ts-node-esm.js - checksum: 10/560d3995ffb9bc69956e6fecc9c3fe576397a0674909ba753b235418e72d9e433fe14ddfb82af4975ceb6a441579b1ea19aefe5811ccb991d879834e05cfa093 - languageName: node - linkType: hard - -"typescript-json-schema@npm:^0.67.0": - version: 0.67.1 - resolution: "typescript-json-schema@npm:0.67.1" - dependencies: - "@types/json-schema": "npm:^7.0.9" - "@types/node": "npm:^18.11.9" - glob: "npm:^7.1.7" - path-equal: "npm:^1.2.5" - safe-stable-stringify: "npm:^2.2.0" - ts-node: "npm:^10.9.1" - typescript: "npm:~5.5.0" - vm2: "npm:^3.10.0" - yargs: "npm:^17.1.1" - bin: - typescript-json-schema: bin/typescript-json-schema - checksum: 10/b7c89c380ad3d8bd281fc5714ae140c8e8f9c491b8666ec3cb28bf16fa8f72c8f7b52c89a7ecef7dc52a233c4bc57d3c0f2c3c3f9d4b096c3432f45209696f0a - languageName: node - linkType: hard - "typescript@npm:5.8.2": version: 5.8.2 resolution: "typescript@npm:5.8.2" @@ -27335,13 +26949,13 @@ __metadata: languageName: node linkType: hard -"typescript@npm:~5.5.0": - version: 5.5.4 - resolution: "typescript@npm:5.5.4" +"typescript@npm:^5.9.3": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/1689ccafef894825481fc3d856b4834ba3cc185a9c2878f3c76a9a1ef81af04194849840f3c69e7961e2312771471bb3b460ca92561e1d87599b26c37d0ffb6f + checksum: 10/c089d9d3da2729fd4ac517f9b0e0485914c4b3c26f80dc0cffcb5de1719a17951e92425d55db59515c1a7ddab65808466debb864d0d56dcf43f27007d0709594 languageName: node linkType: hard @@ -27365,13 +26979,13 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A~5.5.0#optional!builtin": - version: 5.5.4 - resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin::version=5.5.4&hash=379a07" +"typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/746fdd0865c5ce4f15e494c57ede03a9e12ede59cfdb40da3a281807853fe63b00ef1c912d7222143499aa82f18b8b472baa1830df8804746d09b55f6cf5b1cc + checksum: 10/696e1b017bc2635f4e0c94eb4435357701008e2f272f553d06e35b494b8ddc60aa221145e286c28ace0c89ee32827a28c2040e3a69bdc108b1a5dc8fb40b72e3 languageName: node linkType: hard @@ -27459,10 +27073,10 @@ __metadata: languageName: node linkType: hard -"undici@npm:7.28.0, undici@npm:^7.16.0, undici@npm:^7.24.0, undici@npm:^7.24.5": - version: 7.28.0 - resolution: "undici@npm:7.28.0" - checksum: 10/154423b280d623278a61decb437f8a7e581fb18b8c95556ef956b32a58cd668eadbb812d28e20678cb2dc545a566f35a3afc0962307ca801da30f4741117986d +"undici@npm:7.29.0, undici@npm:^7.16.0, undici@npm:^7.24.0, undici@npm:^7.24.5": + version: 7.29.0 + resolution: "undici@npm:7.29.0" + checksum: 10/ca73639071bdcbc41e57be1800847fa18f86f8fbf7a5641ff9899f7bbb6f5cecdd593e225f5fd94e31703dcbfaea4a722a3b81cab90c5c4e2b406aec1ae55732 languageName: node linkType: hard @@ -27684,13 +27298,6 @@ __metadata: languageName: node linkType: hard -"upath@npm:2.0.1": - version: 2.0.1 - resolution: "upath@npm:2.0.1" - checksum: 10/7b98a83559a295d59f87f7a8d615c7549d19e4aec4dd9d52be2bf1ba93e1d6ee7d8f2188cdecbf303a22cea3768abff4268b960350152a0264125f577d9ed79e - languageName: node - linkType: hard - "update-browserslist-db@npm:^1.2.3": version: 1.2.3 resolution: "update-browserslist-db@npm:1.2.3" @@ -27837,15 +27444,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^11.1.1": - version: 11.1.1 - resolution: "uuid@npm:11.1.1" - bin: - uuid: dist/esm/bin/uuid - checksum: 10/16411d3dc12a08d6691616c09a75e66a7f900ba1beef6628a76fe0602f82fae2ee537b564d0b7bc95c24f58d059ca9b58c75a1e806118efb50e17822ff00ddd2 - languageName: node - linkType: hard - "uuid@npm:^3.4.0": version: 3.4.0 resolution: "uuid@npm:3.4.0" @@ -27887,13 +27485,6 @@ __metadata: languageName: node linkType: hard -"v8-compile-cache-lib@npm:^3.0.1": - version: 3.0.1 - resolution: "v8-compile-cache-lib@npm:3.0.1" - checksum: 10/88d3423a52b6aaf1836be779cab12f7016d47ad8430dffba6edf766695e6d90ad4adaa3d8eeb512cc05924f3e246c4a4ca51e089dccf4402caa536b5e5be8961 - languageName: node - linkType: hard - "v8-to-istanbul@npm:^9.0.1": version: 9.3.0 resolution: "v8-to-istanbul@npm:9.3.0" @@ -27998,18 +27589,6 @@ __metadata: languageName: node linkType: hard -"vm2@npm:^3.10.0": - version: 3.10.3 - resolution: "vm2@npm:3.10.3" - dependencies: - acorn: "npm:^8.14.1" - acorn-walk: "npm:^8.3.4" - bin: - vm2: bin/vm2 - checksum: 10/015edd8adda9530b081dacc0eff1e1c7118a03a204337b7ab4b708f435c625aedb0b95637a171fe1022b434ca655dc2b92372784b2c26347d3fc57264fab5824 - languageName: node - linkType: hard - "w3c-xmlserializer@npm:^5.0.0": version: 5.0.0 resolution: "w3c-xmlserializer@npm:5.0.0" @@ -28381,7 +27960,7 @@ __metadata: languageName: node linkType: hard -"which@npm:^1.2.14, which@npm:^1.2.9, which@npm:^1.3.1": +"which@npm:^1.2.9, which@npm:^1.3.1": version: 1.3.1 resolution: "which@npm:1.3.1" dependencies: @@ -28669,7 +28248,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.1.1, yargs@npm:^17.3.1, yargs@npm:^17.7.2": +"yargs@npm:^17.3.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: @@ -28711,13 +28290,6 @@ __metadata: languageName: node linkType: hard -"yn@npm:3.1.1": - version: 3.1.1 - resolution: "yn@npm:3.1.1" - checksum: 10/2c487b0e149e746ef48cda9f8bad10fc83693cd69d7f9dcd8be4214e985de33a29c9e24f3c0d6bcf2288427040a8947406ab27f7af67ee9456e6b84854f02dd6 - languageName: node - linkType: hard - "yn@npm:^4.0.0": version: 4.0.0 resolution: "yn@npm:4.0.0" @@ -28780,15 +28352,6 @@ __metadata: languageName: node linkType: hard -"zod-validation-error@npm:^4.0.2": - version: 4.0.2 - resolution: "zod-validation-error@npm:4.0.2" - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - checksum: 10/5e35ca8ebb4602dcb526e122d7e9fca695c4a479bd97535f3400a732d49160f24f7213a9ed64986fc9dc3a2e8a6c4e1241ec0c4d8a4e3e69ea91a0328ded2192 - languageName: node - linkType: hard - "zod-validation-error@npm:^5.0.0": version: 5.0.0 resolution: "zod-validation-error@npm:5.0.0"