diff --git a/apps/nestjs-backend/package.json b/apps/nestjs-backend/package.json index f919f8526..044864eea 100644 --- a/apps/nestjs-backend/package.json +++ b/apps/nestjs-backend/package.json @@ -142,8 +142,8 @@ "@opentelemetry/resources": "1.27.0", "@opentelemetry/sdk-node": "0.54.2", "@opentelemetry/semantic-conventions": "1.27.0", - "@prisma/client": "5.11.0", - "@prisma/instrumentation": "5.11.0", + "@prisma/client": "6.2.1", + "@prisma/instrumentation": "6.2.1", "@teable/common-i18n": "workspace:^", "@teable/core": "workspace:^", "@teable/db-main-prisma": "workspace:^", diff --git a/apps/nestjs-backend/src/db-provider/db.provider.interface.ts b/apps/nestjs-backend/src/db-provider/db.provider.interface.ts index fd87fd1e5..56177cac7 100644 --- a/apps/nestjs-backend/src/db-provider/db.provider.interface.ts +++ b/apps/nestjs-backend/src/db-provider/db.provider.interface.ts @@ -9,6 +9,7 @@ import type { IAggregationQueryInterface } from './aggregation-query/aggregation import type { BaseQueryAbstract } from './base-query/abstract'; import type { IFilterQueryInterface } from './filter-query/filter-query.interface'; import type { IGroupQueryExtra, IGroupQueryInterface } from './group-query/group-query.interface'; +import type { IntegrityQueryAbstract } from './integrity-query/abstract'; import type { ISortQueryInterface } from './sort-query/sort-query.interface'; export type IFilterQueryExtra = { @@ -160,6 +161,8 @@ export interface IDbProvider { baseQuery(): BaseQueryAbstract; + integrityQuery(): IntegrityQueryAbstract; + calendarDailyCollectionQuery( qb: Knex.QueryBuilder, props: ICalendarDailyCollectionQueryProps diff --git a/apps/nestjs-backend/src/db-provider/integrity-query/abstract.ts b/apps/nestjs-backend/src/db-provider/integrity-query/abstract.ts new file mode 100644 index 000000000..420cb5160 --- /dev/null +++ b/apps/nestjs-backend/src/db-provider/integrity-query/abstract.ts @@ -0,0 +1,31 @@ +import type { Knex } from 'knex'; + +export abstract class IntegrityQueryAbstract { + constructor(protected readonly knex: Knex) {} + + abstract checkLinks(params: { + dbTableName: string; + fkHostTableName: string; + selfKeyName: string; + foreignKeyName: string; + linkDbFieldName: string; + isMultiValue: boolean; + }): string; + + abstract fixLinks(params: { + dbTableName: string; + fkHostTableName: string; + selfKeyName: string; + foreignKeyName: string; + linkDbFieldName: string; + isMultiValue: boolean; + }): string; + + abstract updateJsonField(params: { + recordIds: string[]; + dbTableName: string; + field: string; + value: string | number | boolean | null; + arrayIndex?: number; + }): Knex.QueryBuilder; +} diff --git a/apps/nestjs-backend/src/db-provider/integrity-query/integrity-query.postgres.ts b/apps/nestjs-backend/src/db-provider/integrity-query/integrity-query.postgres.ts new file mode 100644 index 000000000..6937befbb --- /dev/null +++ b/apps/nestjs-backend/src/db-provider/integrity-query/integrity-query.postgres.ts @@ -0,0 +1,218 @@ +import type { Knex } from 'knex'; +import { IntegrityQueryAbstract } from './abstract'; + +export class IntegrityQueryPostgres extends IntegrityQueryAbstract { + constructor(protected readonly knex: Knex) { + super(knex); + } + + checkLinks({ + dbTableName, + fkHostTableName, + selfKeyName, + foreignKeyName, + linkDbFieldName, + isMultiValue, + }: { + dbTableName: string; + fkHostTableName: string; + selfKeyName: string; + foreignKeyName: string; + linkDbFieldName: string; + isMultiValue: boolean; + }): string { + if (isMultiValue) { + const fkGroupedQuery = this.knex(fkHostTableName) + .select({ + [selfKeyName]: selfKeyName, + fk_ids: this.knex.raw(`string_agg(??, ',' ORDER BY ??)`, [ + this.knex.ref(foreignKeyName), + this.knex.ref(foreignKeyName), + ]), + }) + .whereNotNull(selfKeyName) + .groupBy(selfKeyName) + .as('fk_grouped'); + const thisKnex = this.knex; + return this.knex(dbTableName) + .leftJoin(fkGroupedQuery, `${dbTableName}.__id`, `fk_grouped.${selfKeyName}`) + .select({ + id: '__id', + }) + .where(function () { + this.whereNull(`fk_grouped.${selfKeyName}`) + .whereNotNull(linkDbFieldName) + .orWhere(function () { + this.whereNotNull(linkDbFieldName).andWhereRaw( + `"fk_grouped".fk_ids != ( + SELECT string_agg(id, ',' ORDER BY id) + FROM ( + SELECT (link->>'id')::text as id + FROM jsonb_array_elements(??::jsonb) as link + ) t + )`, + [thisKnex.ref(linkDbFieldName)] + ); + }); + }) + .toQuery(); + } + + if (fkHostTableName === dbTableName) { + return this.knex(dbTableName) + .select({ + id: '__id', + }) + .where(function () { + this.whereNull(foreignKeyName) + .whereNotNull(linkDbFieldName) + .orWhere(function () { + this.whereNotNull(linkDbFieldName).andWhereRaw( + `("${linkDbFieldName}"->>'id')::text != "${foreignKeyName}"::text` + ); + }); + }) + .toQuery(); + } + + if (dbTableName === fkHostTableName) { + return this.knex(`${dbTableName} as t1`) + .select({ + id: 't1.__id', + }) + .leftJoin(`${dbTableName} as t2`, 't2.' + foreignKeyName, 't1.__id') + .where(function () { + this.whereNull('t2.' + foreignKeyName) + .whereNotNull('t1.' + linkDbFieldName) + .orWhere(function () { + this.whereNotNull('t1.' + linkDbFieldName).andWhereRaw( + `("t1"."${linkDbFieldName}"->>'id')::text != "t2"."${foreignKeyName}"::text` + ); + }); + }) + .toQuery(); + } + + return this.knex(`${dbTableName} as t1`) + .select({ + id: 't1.__id', + }) + .leftJoin(`${fkHostTableName} as t2`, 't2.' + selfKeyName, 't1.__id') + .where(function () { + this.whereNull('t2.' + foreignKeyName) + .whereNotNull('t1.' + linkDbFieldName) + .orWhere(function () { + this.whereNotNull('t1.' + linkDbFieldName).andWhereRaw( + `("t1"."${linkDbFieldName}"->>'id')::text != "t2"."${foreignKeyName}"::text` + ); + }); + }) + .toQuery(); + } + + fixLinks({ + recordIds, + dbTableName, + foreignDbTableName, + fkHostTableName, + lookupDbFieldName, + selfKeyName, + foreignKeyName, + linkDbFieldName, + isMultiValue, + }: { + recordIds: string[]; + dbTableName: string; + foreignDbTableName: string; + fkHostTableName: string; + lookupDbFieldName: string; + selfKeyName: string; + foreignKeyName: string; + linkDbFieldName: string; + isMultiValue: boolean; + }): string { + if (isMultiValue) { + return this.knex(dbTableName) + .update({ + [linkDbFieldName]: this.knex + .select( + this.knex.raw("jsonb_agg(jsonb_build_object('id', ??, 'title', ??) ORDER BY ??)", [ + `fk.${foreignKeyName}`, + `ft.${lookupDbFieldName}`, + `fk.${foreignKeyName}`, + ]) + ) + .from(`${fkHostTableName} as fk`) + .join(`${foreignDbTableName} as ft`, `ft.__id`, `fk.${foreignKeyName}`) + .where('fk.' + selfKeyName, `${dbTableName}.__id`), + }) + .whereIn('__id', recordIds) + .toQuery(); + } + + if (fkHostTableName === dbTableName) { + // Handle self-referential single-value links + return this.knex(dbTableName) + .update({ + [linkDbFieldName]: this.knex.raw( + ` + CASE + WHEN ?? IS NULL THEN NULL + ELSE jsonb_build_object( + 'id', ??, + 'title', ?? + ) + END + `, + [foreignKeyName, foreignKeyName, lookupDbFieldName] + ), + }) + .whereIn('__id', recordIds) + .toQuery(); + } + + // Handle cross-table single-value links + return this.knex(dbTableName) + .update({ + [linkDbFieldName]: this.knex + .select( + this.knex.raw( + `CASE + WHEN t2.?? IS NULL THEN NULL + ELSE jsonb_build_object('id', t2.??, 'title', t2.??) + END`, + [foreignKeyName, foreignKeyName, lookupDbFieldName] + ) + ) + .from(`${fkHostTableName} as t2`) + .where(`t2.${foreignKeyName}`, `${dbTableName}.__id`) + .limit(1), + }) + .whereIn('__id', recordIds) + .toQuery(); + } + + updateJsonField({ + recordIds, + dbTableName, + field, + value, + arrayIndex, + }: { + recordIds: string[]; + dbTableName: string; + field: string; + value: string | number | boolean | null; + arrayIndex?: number; + }) { + return this.knex(dbTableName) + .whereIn('__id', recordIds) + .update({ + [field]: this.knex.raw(`jsonb_set( + "${field}", + '${arrayIndex != null ? `{${arrayIndex},id}` : '{id}'}', + '${JSON.stringify(value)}' + )`), + }); + } +} diff --git a/apps/nestjs-backend/src/db-provider/integrity-query/integrity-query.sqlite.ts b/apps/nestjs-backend/src/db-provider/integrity-query/integrity-query.sqlite.ts new file mode 100644 index 000000000..76787abba --- /dev/null +++ b/apps/nestjs-backend/src/db-provider/integrity-query/integrity-query.sqlite.ts @@ -0,0 +1,242 @@ +import type { Knex } from 'knex'; +import { IntegrityQueryAbstract } from './abstract'; + +export class IntegrityQuerySqlite extends IntegrityQueryAbstract { + constructor(protected readonly knex: Knex) { + super(knex); + } + + checkLinks({ + dbTableName, + fkHostTableName, + selfKeyName, + foreignKeyName, + linkDbFieldName, + isMultiValue, + }: { + dbTableName: string; + fkHostTableName: string; + selfKeyName: string; + foreignKeyName: string; + linkDbFieldName: string; + isMultiValue: boolean; + }): string { + const thisKnex = this.knex; + if (isMultiValue) { + const fkGroupedQuery = this.knex(fkHostTableName) + .select({ + [selfKeyName]: selfKeyName, + fk_ids: this.knex.raw(`GROUP_CONCAT(??)`, [this.knex.ref(foreignKeyName)]), + }) + .whereNotNull(selfKeyName) + .groupBy(selfKeyName) + .as('fk_grouped'); + return this.knex(dbTableName) + .leftJoin(fkGroupedQuery, `${dbTableName}.__id`, `fk_grouped.${selfKeyName}`) + .select({ + id: '__id', + }) + .where(function () { + this.whereNull(`fk_grouped.${selfKeyName}`) + .whereNotNull(linkDbFieldName) + .orWhere(function () { + this.whereNotNull(linkDbFieldName).andWhereRaw( + `"fk_grouped".fk_ids != ( + SELECT GROUP_CONCAT(id) + FROM ( + SELECT json_extract(link.value, '$.id') as id + FROM json_each(?) as link + ) t + )`, + [thisKnex.ref(linkDbFieldName)] + ); + }); + }) + .toQuery(); + } + + if (fkHostTableName === dbTableName) { + return this.knex(dbTableName) + .select({ + id: '__id', + }) + .where(function () { + this.whereNull(foreignKeyName) + .whereNotNull(linkDbFieldName) + .orWhere(function () { + this.whereNotNull(linkDbFieldName).andWhereRaw( + `json_extract(??, '$.id') != CAST(${foreignKeyName} AS TEXT)`, + [thisKnex.ref(linkDbFieldName)] + ); + }); + }) + .toQuery(); + } + + if (dbTableName === fkHostTableName) { + return this.knex(`${dbTableName} as t1`) + .select({ + id: 't1.__id', + }) + .leftJoin(`${dbTableName} as t2`, 't2.' + foreignKeyName, 't1.__id') + .where(function () { + this.whereNull('t2.' + foreignKeyName) + .whereNotNull('t1.' + linkDbFieldName) + .orWhere(function () { + this.whereNotNull('t1.' + linkDbFieldName).andWhereRaw( + `json_extract(t1."${linkDbFieldName}", '$.id') != CAST(t2."${foreignKeyName}" AS TEXT)` + ); + }); + }) + .toQuery(); + } + + return this.knex(`${dbTableName} as t1`) + .select({ + id: 't1.__id', + }) + .leftJoin(`${fkHostTableName} as t2`, 't2.' + selfKeyName, 't1.__id') + .where(function () { + this.whereNull('t2.' + foreignKeyName) + .whereNotNull('t1.' + linkDbFieldName) + .orWhere(function () { + this.whereNotNull('t1.' + linkDbFieldName).andWhereRaw( + `json_extract(t1."${linkDbFieldName}", '$.id') != CAST(t2."${foreignKeyName}" AS TEXT)` + ); + }); + }) + .toQuery(); + } + + fixLinks({ + recordIds, + dbTableName, + foreignDbTableName, + fkHostTableName, + lookupDbFieldName, + selfKeyName, + foreignKeyName, + linkDbFieldName, + isMultiValue, + }: { + recordIds: string[]; + dbTableName: string; + foreignDbTableName: string; + fkHostTableName: string; + lookupDbFieldName: string; + selfKeyName: string; + foreignKeyName: string; + linkDbFieldName: string; + isMultiValue: boolean; + }): string { + if (isMultiValue) { + return this.knex(dbTableName) + .update({ + [linkDbFieldName]: this.knex + .select( + this.knex.raw( + `json_group_array( + json_object( + 'id', fk.${foreignKeyName}, + 'title', ft.${lookupDbFieldName} + ) + )` + ) + ) + .from(`${fkHostTableName} as fk`) + .join(`${foreignDbTableName} as ft`, `ft.__id`, `fk.${foreignKeyName}`) + .where('fk.' + selfKeyName, `${dbTableName}.__id`) + .orderBy(`fk.${foreignKeyName}`), + }) + .whereIn('__id', recordIds) + .toQuery(); + } + + if (fkHostTableName === dbTableName) { + // Handle self-referential single-value links + return this.knex(dbTableName) + .update({ + [linkDbFieldName]: this.knex.raw( + ` + CASE + WHEN ?? IS NULL THEN NULL + ELSE json_object( + 'id', ??, + 'title', ?? + ) + END + `, + [foreignKeyName, foreignKeyName, lookupDbFieldName] + ), + }) + .whereIn('__id', recordIds) + .toQuery(); + } + + // Handle cross-table single-value links + return this.knex(dbTableName) + .update({ + [linkDbFieldName]: this.knex + .select( + this.knex.raw( + `CASE + WHEN t2.?? IS NULL THEN NULL + ELSE json_object('id', t2.??, 'title', t2.??) + END`, + [foreignKeyName, foreignKeyName, lookupDbFieldName] + ) + ) + .from(`${fkHostTableName} as t2`) + .where(`t2.${foreignKeyName}`, `${dbTableName}.__id`) + .limit(1), + }) + .whereIn('__id', recordIds) + .toQuery(); + } + + updateJsonField({ + recordIds, + dbTableName, + field, + value, + arrayIndex, + }: { + recordIds: string[]; + dbTableName: string; + field: string; + value: string | number | boolean | null; + arrayIndex?: number; + }) { + if (arrayIndex != null) { + // For array elements, we need to use json_replace with json_extract + return this.knex(dbTableName) + .whereIn('__id', recordIds) + .update({ + [field]: this.knex.raw( + ` + json_replace( + "${field}", + '$[' || ? || '].id', + json(?)) + `, + [arrayIndex, JSON.stringify(value)] + ), + }); + } + + // For single value + return this.knex(dbTableName) + .whereIn('__id', recordIds) + .update({ + [field]: this.knex.raw( + ` + json_replace( + "${field}", + '$.id', + json(?)) + `, + [JSON.stringify(value)] + ), + }); + } +} diff --git a/apps/nestjs-backend/src/db-provider/postgres.provider.ts b/apps/nestjs-backend/src/db-provider/postgres.provider.ts index bae138547..993ebfdba 100644 --- a/apps/nestjs-backend/src/db-provider/postgres.provider.ts +++ b/apps/nestjs-backend/src/db-provider/postgres.provider.ts @@ -22,6 +22,8 @@ import type { IFilterQueryInterface } from './filter-query/filter-query.interfac import { FilterQueryPostgres } from './filter-query/postgres/filter-query.postgres'; import type { IGroupQueryExtra, IGroupQueryInterface } from './group-query/group-query.interface'; import { GroupQueryPostgres } from './group-query/group-query.postgres'; +import type { IntegrityQueryAbstract } from './integrity-query/abstract'; +import { IntegrityQueryPostgres } from './integrity-query/integrity-query.postgres'; import { SearchQueryAbstract } from './search-query/abstract'; import { SearchQueryBuilder, SearchQueryPostgres } from './search-query/search-query.postgres'; import { SortQueryPostgres } from './sort-query/postgres/sort-query.postgres'; @@ -379,6 +381,10 @@ export class PostgresProvider implements IDbProvider { return new BaseQueryPostgres(this.knex); } + integrityQuery(): IntegrityQueryAbstract { + return new IntegrityQueryPostgres(this.knex); + } + calendarDailyCollectionQuery( qb: Knex.QueryBuilder, props: ICalendarDailyCollectionQueryProps diff --git a/apps/nestjs-backend/src/db-provider/sqlite.provider.ts b/apps/nestjs-backend/src/db-provider/sqlite.provider.ts index 143881cde..9ff4bc680 100644 --- a/apps/nestjs-backend/src/db-provider/sqlite.provider.ts +++ b/apps/nestjs-backend/src/db-provider/sqlite.provider.ts @@ -22,6 +22,8 @@ import type { IFilterQueryInterface } from './filter-query/filter-query.interfac import { FilterQuerySqlite } from './filter-query/sqlite/filter-query.sqlite'; import type { IGroupQueryExtra, IGroupQueryInterface } from './group-query/group-query.interface'; import { GroupQuerySqlite } from './group-query/group-query.sqlite'; +import type { IntegrityQueryAbstract } from './integrity-query/abstract'; +import { IntegrityQuerySqlite } from './integrity-query/integrity-query.sqlite'; import { SearchQueryAbstract } from './search-query/abstract'; import { getOffset } from './search-query/get-offset'; import { SearchQueryBuilder, SearchQuerySqlite } from './search-query/search-query.sqlite'; @@ -148,15 +150,19 @@ export class SqliteProvider implements IDbProvider { .update({ [columnName]: this.knex.raw( ` - ( - SELECT json_group_array( - CASE - WHEN json_extract(value, '$.id') = ? - THEN json_patch(value, json_object(?, ?)) - ELSE value - END + json( + ( + SELECT json_group_array( + json( + CASE + WHEN json_extract(value, '$.id') = ? + THEN json_patch(value, json_object(?, ?)) + ELSE value + END + ) + ) + FROM json_each(${columnName}) ) - FROM json_each(${columnName}) ) `, [id, key, value] @@ -332,6 +338,10 @@ export class SqliteProvider implements IDbProvider { return new BaseQuerySqlite(this.knex); } + integrityQuery(): IntegrityQueryAbstract { + return new IntegrityQuerySqlite(this.knex); + } + calendarDailyCollectionQuery( qb: Knex.QueryBuilder, props: ICalendarDailyCollectionQueryProps diff --git a/apps/nestjs-backend/src/features/collaborator/collaborator.service.ts b/apps/nestjs-backend/src/features/collaborator/collaborator.service.ts index bedd22904..8ebcf4265 100644 --- a/apps/nestjs-backend/src/features/collaborator/collaborator.service.ts +++ b/apps/nestjs-backend/src/features/collaborator/collaborator.service.ts @@ -97,7 +97,8 @@ export class CollaboratorService { ) .into('collaborator') .toQuery(); - await this.prismaService.$executeRawUnsafe(query); + + await this.prismaService.txClient().$executeRawUnsafe(query); this.eventEmitterService.emitAsync( Events.COLLABORATOR_CREATE, new CollaboratorCreateEvent(spaceId) @@ -676,7 +677,7 @@ export class CollaboratorService { .into('collaborator') .toQuery(); - const res = await this.prismaService.$executeRawUnsafe(query); + const res = await this.prismaService.txClient().$executeRawUnsafe(query); this.eventEmitterService.emitAsync( Events.COLLABORATOR_CREATE, new CollaboratorCreateEvent(base.spaceId) diff --git a/apps/nestjs-backend/src/features/integrity/link-field.service.ts b/apps/nestjs-backend/src/features/integrity/link-field.service.ts index 47cd56b3a..967552cb7 100644 --- a/apps/nestjs-backend/src/features/integrity/link-field.service.ts +++ b/apps/nestjs-backend/src/features/integrity/link-field.service.ts @@ -2,8 +2,8 @@ import { Injectable, Logger } from '@nestjs/common'; import { FieldType, type ILinkFieldOptions } from '@teable/core'; import { PrismaService } from '@teable/db-main-prisma'; import { IntegrityIssueType, type IIntegrityIssue } from '@teable/openapi'; -import { Knex } from 'knex'; -import { InjectModel } from 'nest-knexjs'; +import { InjectDbProvider } from '../../db-provider/db.provider'; +import { IDbProvider } from '../../db-provider/db.provider.interface'; import { createFieldInstanceByRaw } from '../field/model/factory'; import type { LinkFieldDto } from '../field/model/field-dto/link-field.dto'; @@ -13,7 +13,7 @@ export class LinkFieldIntegrityService { constructor( private readonly prismaService: PrismaService, - @InjectModel('CUSTOM_KNEX') private readonly knex: Knex + @InjectDbProvider() private readonly dbProvider: IDbProvider ) {} async getIssues(tableId: string, field: LinkFieldDto): Promise { @@ -43,14 +43,7 @@ export class LinkFieldIntegrityService { return []; } - private async checkLinks({ - dbTableName, - fkHostTableName, - selfKeyName, - foreignKeyName, - linkDbFieldName, - isMultiValue, - }: { + private async checkLinks(params: { dbTableName: string; fkHostTableName: string; selfKeyName: string; @@ -58,130 +51,11 @@ export class LinkFieldIntegrityService { linkDbFieldName: string; isMultiValue: boolean; }) { - if (isMultiValue) { - const fkGroupedQuery = this.knex(fkHostTableName) - .select({ - [selfKeyName]: selfKeyName, - fk_ids: this.knex.raw(`string_agg(??, ',' ORDER BY ??)`, [ - this.knex.ref(foreignKeyName), - this.knex.ref(foreignKeyName), - ]), - }) - .whereNotNull(selfKeyName) - .groupBy(selfKeyName) - .as('fk_grouped'); - const thisKnex = this.knex; - const query = this.knex(dbTableName) - .leftJoin(fkGroupedQuery, `${dbTableName}.__id`, `fk_grouped.${selfKeyName}`) - .select({ - id: '__id', - }) - .where(function () { - this.whereNull(`fk_grouped.${selfKeyName}`) - .whereNotNull(linkDbFieldName) - .orWhere(function () { - this.whereNotNull(linkDbFieldName).andWhereRaw( - `"fk_grouped".fk_ids != ( - SELECT string_agg(id, ',' ORDER BY id) - FROM ( - SELECT (link->>'id')::text as id - FROM jsonb_array_elements(??::jsonb) as link - ) t - )`, - [thisKnex.ref(linkDbFieldName)] - ); - }); - }) - .toQuery(); - - return await this.prismaService.$queryRawUnsafe< - { - id: string; - }[] - >(query); - } - - if (fkHostTableName === dbTableName) { - const query = this.knex(dbTableName) - .select({ - id: '__id', - }) - .where(function () { - this.whereNull(foreignKeyName) - .whereNotNull(linkDbFieldName) - .orWhere(function () { - this.whereNotNull(linkDbFieldName).andWhereRaw( - `("${linkDbFieldName}"->>'id')::text != "${foreignKeyName}"::text` - ); - }); - }) - .toQuery(); - - return await this.prismaService.$queryRawUnsafe< - { - id: string; - }[] - >(query); - } - - if (dbTableName === fkHostTableName) { - const query = this.knex(`${dbTableName} as t1`) - .select({ - id: 't1.__id', - }) - .leftJoin(`${dbTableName} as t2`, 't2.' + foreignKeyName, 't1.__id') - .where(function () { - this.whereNull('t2.' + foreignKeyName) - .whereNotNull('t1.' + linkDbFieldName) - .orWhere(function () { - this.whereNotNull('t1.' + linkDbFieldName).andWhereRaw( - `("t1"."${linkDbFieldName}"->>'id')::text != "t2"."${foreignKeyName}"::text` - ); - }); - }) - .toQuery(); - - return await this.prismaService.$queryRawUnsafe< - { - id: string; - }[] - >(query); - } - - const query = this.knex(`${dbTableName} as t1`) - .select({ - id: 't1.__id', - }) - .leftJoin(`${fkHostTableName} as t2`, 't2.' + selfKeyName, 't1.__id') - .where(function () { - this.whereNull('t2.' + foreignKeyName) - .whereNotNull('t1.' + linkDbFieldName) - .orWhere(function () { - this.whereNotNull('t1.' + linkDbFieldName).andWhereRaw( - `("t1"."${linkDbFieldName}"->>'id')::text != "t2"."${foreignKeyName}"::text` - ); - }); - }) - .toQuery(); - - return await this.prismaService.$queryRawUnsafe< - { - id: string; - }[] - >(query); + const query = this.dbProvider.integrityQuery().checkLinks(params); + return await this.prismaService.$queryRawUnsafe<{ id: string }[]>(query); } - private async fixLinks({ - recordIds, - dbTableName, - foreignDbTableName, - fkHostTableName, - lookupDbFieldName, - selfKeyName, - foreignKeyName, - linkDbFieldName, - isMultiValue, - }: { + private async fixLinks(params: { recordIds: string[]; dbTableName: string; foreignDbTableName: string; @@ -192,70 +66,7 @@ export class LinkFieldIntegrityService { linkDbFieldName: string; isMultiValue: boolean; }) { - if (isMultiValue) { - const query = this.knex(dbTableName) - .update({ - [linkDbFieldName]: this.knex - .select( - this.knex.raw("jsonb_agg(jsonb_build_object('id', ??, 'title', ??) ORDER BY ??)", [ - `fk.${foreignKeyName}`, - `ft.${lookupDbFieldName}`, - `fk.${foreignKeyName}`, - ]) - ) - .from(`${fkHostTableName} as fk`) - .join(`${foreignDbTableName} as ft`, `ft.__id`, `fk.${foreignKeyName}`) - .where('fk.' + selfKeyName, `${dbTableName}.__id`), - }) - .whereIn('__id', recordIds) - .toQuery(); - - return await this.prismaService.$executeRawUnsafe(query); - } - - if (fkHostTableName === dbTableName) { - // Handle self-referential single-value links - const query = this.knex(dbTableName) - .update({ - [linkDbFieldName]: this.knex.raw( - ` - CASE - WHEN ?? IS NULL THEN NULL - ELSE jsonb_build_object( - 'id', ??, - 'title', ?? - ) - END - `, - [foreignKeyName, foreignKeyName, lookupDbFieldName] - ), - }) - .whereIn('__id', recordIds) - .toQuery(); - - return await this.prismaService.$executeRawUnsafe(query); - } - - // Handle cross-table single-value links - const query = this.knex(dbTableName) - .update({ - [linkDbFieldName]: this.knex - .select( - this.knex.raw( - `CASE - WHEN t2.?? IS NULL THEN NULL - ELSE jsonb_build_object('id', t2.??, 'title', t2.??) - END`, - [foreignKeyName, foreignKeyName, lookupDbFieldName] - ) - ) - .from(`${fkHostTableName} as t2`) - .where(`t2.${foreignKeyName}`, `${dbTableName}.__id`) - .limit(1), - }) - .whereIn('__id', recordIds) - .toQuery(); - + const query = this.dbProvider.integrityQuery().fixLinks(params); return await this.prismaService.$executeRawUnsafe(query); } diff --git a/apps/nestjs-backend/src/features/view/view.service.ts b/apps/nestjs-backend/src/features/view/view.service.ts index 1de7fcd61..833ffbfeb 100644 --- a/apps/nestjs-backend/src/features/view/view.service.ts +++ b/apps/nestjs-backend/src/features/view/view.service.ts @@ -203,7 +203,7 @@ export class ViewService implements IReadonlyAdapterService { async restoreView(tableId: string, viewId: string) { await this.prismaService.$tx(async () => { - await this.prismaService.view.update({ + await this.prismaService.txClient().view.update({ where: { id: viewId }, data: { deletedTime: null, diff --git a/apps/nestjs-backend/test/integrity.e2e-spec.ts b/apps/nestjs-backend/test/integrity.e2e-spec.ts index 444e6ca65..2c5bbddb0 100644 --- a/apps/nestjs-backend/test/integrity.e2e-spec.ts +++ b/apps/nestjs-backend/test/integrity.e2e-spec.ts @@ -20,6 +20,8 @@ import { updateRecords, } from '@teable/openapi'; import type { Knex } from 'knex'; +import { DB_PROVIDER_SYMBOL } from '../src/db-provider/db.provider'; +import type { IDbProvider } from '../src/db-provider/db.provider.interface'; import { createField, createTable, @@ -35,6 +37,7 @@ describe('OpenAPI integrity (e2e)', () => { let prisma: PrismaService; let db: Knex; + let dbProvider: IDbProvider; async function executeKnex(builder: Knex.SchemaBuilder | Knex.QueryBuilder) { const query = builder.toQuery(); @@ -44,6 +47,7 @@ describe('OpenAPI integrity (e2e)', () => { beforeAll(async () => { const appCtx = await initApp(); db = appCtx.app.get('CUSTOM_KNEX'); + dbProvider = appCtx.app.get(DB_PROVIDER_SYMBOL); prisma = appCtx.app.get(PrismaService); app = appCtx.app; }); @@ -220,15 +224,13 @@ describe('OpenAPI integrity (e2e)', () => { // test multiple link await executeKnex( - db(base2table2.dbTableName) - .where('__id', base2table2.records[0].id) - .update({ - [symLinkField.dbFieldName]: db.raw(`jsonb_set( - "${symLinkField.dbFieldName}", - '{0,id}', - '"xxx"' - )`), - }) + dbProvider.integrityQuery().updateJsonField({ + recordIds: [base2table2.records[0].id], + dbTableName: base2table2.dbTableName, + field: symLinkField.dbFieldName, + value: 'xxx', + arrayIndex: 0, + }) ); const record = await getRecord(base2table2.id, base2table2.records[0].id); @@ -248,15 +250,12 @@ describe('OpenAPI integrity (e2e)', () => { // test single link await executeKnex( - db(base2table1.dbTableName) - .where('__id', base2table1.records[0].id) - .update({ - [linkField.dbFieldName]: db.raw(`jsonb_set( - "${linkField.dbFieldName}", - '{id}', - '"xxx"' - )`), - }) + dbProvider.integrityQuery().updateJsonField({ + recordIds: [base2table1.records[0].id], + dbTableName: base2table1.dbTableName, + field: linkField.dbFieldName, + value: 'xxx', + }) ); const record2 = await getRecord(base2table1.id, base2table1.records[0].id); @@ -331,15 +330,12 @@ describe('OpenAPI integrity (e2e)', () => { // test multiple link await executeKnex( - db(base2table2.dbTableName) - .whereIn('__id', [base2table2.records[0].id, base2table2.records[1].id]) - .update({ - [symLinkField.dbFieldName]: db.raw(`jsonb_set( - "${symLinkField.dbFieldName}", - '{id}', - '"xxx"' - )`), - }) + dbProvider.integrityQuery().updateJsonField({ + recordIds: [base2table2.records[0].id, base2table2.records[1].id], + dbTableName: base2table2.dbTableName, + field: symLinkField.dbFieldName, + value: 'xxx', + }) ); const records = await getRecords(base2table2.id); @@ -357,15 +353,12 @@ describe('OpenAPI integrity (e2e)', () => { // test single link await executeKnex( - db(base2table1.dbTableName) - .whereIn('__id', [base2table1.records[0].id, base2table1.records[1].id]) - .update({ - [linkField.dbFieldName]: db.raw(`jsonb_set( - "${linkField.dbFieldName}", - '{id}', - '"xxx"' - )`), - }) + dbProvider.integrityQuery().updateJsonField({ + recordIds: [base2table1.records[0].id, base2table1.records[1].id], + dbTableName: base2table1.dbTableName, + field: linkField.dbFieldName, + value: 'xxx', + }) ); const records2 = await getRecords(base2table1.id); @@ -434,15 +427,13 @@ describe('OpenAPI integrity (e2e)', () => { // test multiple link await executeKnex( - db(base2table2.dbTableName) - .where('__id', base2table2.records[0].id) - .update({ - [symLinkField.dbFieldName]: db.raw(`jsonb_set( - "${symLinkField.dbFieldName}", - '{0,id}', - '"xxx"' - )`), - }) + dbProvider.integrityQuery().updateJsonField({ + recordIds: [base2table2.records[0].id], + dbTableName: base2table2.dbTableName, + field: symLinkField.dbFieldName, + value: 'xxx', + arrayIndex: 0, + }) ); const record = await getRecord(base2table2.id, base2table2.records[0].id); @@ -462,15 +453,13 @@ describe('OpenAPI integrity (e2e)', () => { // test single link await executeKnex( - db(base2table1.dbTableName) - .where('__id', base2table1.records[0].id) - .update({ - [linkField.dbFieldName]: db.raw(`jsonb_set( - "${linkField.dbFieldName}", - '{0,id}', - '"xxx"' - )`), - }) + dbProvider.integrityQuery().updateJsonField({ + recordIds: [base2table1.records[0].id], + dbTableName: base2table1.dbTableName, + field: linkField.dbFieldName, + value: 'xxx', + arrayIndex: 0, + }) ); const record2 = await getRecord(base2table1.id, base2table1.records[0].id); diff --git a/package.json b/package.json index bbf9682ce..bb0299305 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ ], "scripts": { "clean:global-cache": "rimraf ./.cache", - "deps:check": "pnpm --package=npm-check-updates@latest dlx npm-check-updates --configFileName ncurc.yml --workspaces --root --mergeConfig", - "deps:update": "pnpm --package=npm-check-updates@latest dlx npm-check-updates --configFileName ncurc.yml -u --workspaces --root --mergeConfig", + "deps:check": "pnpm --package=npm-check-updates@latest dlx npm-check-updates --configFileName .ncurc.yml --workspaces --root --mergeConfig", + "deps:update": "pnpm --package=npm-check-updates@latest dlx npm-check-updates --configFileName .ncurc.yml -u --workspaces --root --mergeConfig", "g:build": "pnpm -r run build", "g:build-changed": "pnpm -r -F '...[origin/main]' build", "g:check-dist": "pnpm -r --parallel check-dist", diff --git a/packages/db-main-prisma/package.json b/packages/db-main-prisma/package.json index 9379e394f..79b226f1e 100644 --- a/packages/db-main-prisma/package.json +++ b/packages/db-main-prisma/package.json @@ -47,8 +47,8 @@ "nestjs-cls": "^4.0.0" }, "dependencies": { - "@prisma/client": "5.11.0", - "prisma": "5.11.0", + "@prisma/client": "6.2.1", + "prisma": "6.2.1", "nanoid": "3.3.7" }, "devDependencies": { diff --git a/packages/db-main-prisma/prisma/sqlite/schema.prisma b/packages/db-main-prisma/prisma/sqlite/schema.prisma index 74cfcdfa3..c98a088bf 100644 --- a/packages/db-main-prisma/prisma/sqlite/schema.prisma +++ b/packages/db-main-prisma/prisma/sqlite/schema.prisma @@ -1,7 +1,6 @@ generator client { provider = "prisma-client-js" binaryTargets = ["native", "debian-openssl-1.1.x"] - previewFeatures = ["tracing"] } datasource db { diff --git a/packages/db-main-prisma/prisma/template.prisma b/packages/db-main-prisma/prisma/template.prisma index f73556e72..fb1c7ccda 100644 --- a/packages/db-main-prisma/prisma/template.prisma +++ b/packages/db-main-prisma/prisma/template.prisma @@ -1,7 +1,6 @@ generator client { provider = "prisma-client-js" binaryTargets = ["native", "debian-openssl-1.1.x"] - previewFeatures = ["tracing"] } datasource db { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 25bfcc7c1..55d509024 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -125,7 +125,7 @@ importers: version: 7.3.0(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(@nestjs/core@10.3.5)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.2(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1))(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(@nestjs/core@10.3.5)(@prisma/client@5.11.0(prisma@5.11.0))(reflect-metadata@0.2.1)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.2(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1))(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(@nestjs/core@10.3.5)(@prisma/client@6.2.1(prisma@6.2.1))(reflect-metadata@0.2.1)(rxjs@7.8.1) '@nestjs/websockets': specifier: 10.3.5 version: 10.3.5(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(@nestjs/core@10.3.5)(reflect-metadata@0.2.1)(rxjs@7.8.1) @@ -157,11 +157,11 @@ importers: specifier: 1.27.0 version: 1.27.0 '@prisma/client': - specifier: 5.11.0 - version: 5.11.0(prisma@5.11.0) + specifier: 6.2.1 + version: 6.2.1(prisma@6.2.1) '@prisma/instrumentation': - specifier: 5.11.0 - version: 5.11.0 + specifier: 6.2.1 + version: 6.2.1(@opentelemetry/api@1.9.0) '@teable/common-i18n': specifier: workspace:^ version: link:../../packages/common-i18n @@ -1072,8 +1072,8 @@ importers: specifier: ^10.0.0 version: 10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) '@prisma/client': - specifier: 5.11.0 - version: 5.11.0(prisma@5.11.0) + specifier: 6.2.1 + version: 6.2.1(prisma@6.2.1) nanoid: specifier: 3.3.7 version: 3.3.7 @@ -1081,8 +1081,8 @@ importers: specifier: ^4.0.0 version: 4.3.0(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(@nestjs/core@10.3.5)(reflect-metadata@0.2.1)(rxjs@7.8.1) prisma: - specifier: 5.11.0 - version: 5.11.0 + specifier: 6.2.1 + version: 6.2.1 devDependencies: '@faker-js/faker': specifier: 8.4.1 @@ -1161,7 +1161,7 @@ importers: version: 9.1.0(eslint@8.57.0) eslint-import-resolver-typescript: specifier: 3.6.1 - version: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + version: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: specifier: 2.29.1 version: 2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) @@ -1642,7 +1642,7 @@ importers: version: 5.4.3 vite-plugin-svgr: specifier: 4.2.0 - version: 4.2.0(rollup@4.28.1)(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)) + version: 4.2.0(rollup@2.79.2)(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)) vite-tsconfig-paths: specifier: 4.3.2 version: 4.3.2(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)) @@ -4933,18 +4933,10 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} - '@opentelemetry/api-logs@0.49.1': - resolution: {integrity: sha512-kaNl/T7WzyMUQHQlVq7q0oV4Kev6+0xFwqzofryC66jgGMacd0QH5TwfpbUwSTby+SdAdprAe5UKMvBw4tKS5Q==} - engines: {node: '>=14'} - '@opentelemetry/api-logs@0.54.2': resolution: {integrity: sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==} engines: {node: '>=14'} - '@opentelemetry/api@1.8.0': - resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} - engines: {node: '>=8.0.0'} - '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -4955,12 +4947,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@1.22.0': - resolution: {integrity: sha512-0VoAlT6x+Xzik1v9goJ3pZ2ppi6+xd3aUfg4brfrLkDBHRIVjMP0eBHrKrhB+NKcDyMAg8fAbGL3Npg/F6AwWA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' - '@opentelemetry/core@1.27.0': resolution: {integrity: sha512-yQPKnK5e+76XuiqUH/gKyS8wv/7qITd5ln56QkBTf3uggr0VkXOXfcaAuG330UfdYu83wsyoBwqwxigpIG+Jkg==} engines: {node: '>=14'} @@ -5033,12 +5019,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.49.1': - resolution: {integrity: sha512-0DLtWtaIppuNNRRllSD4bjU8ZIiLp1cDXvJEbp752/Zf+y3gaLNaoGRGIlX4UHhcsrmtL+P2qxi3Hodi8VuKiQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.54.2': resolution: {integrity: sha512-go6zpOVoZVztT9r1aPd79Fr3OWiD4N24bCPJsIKkBses8oyFo12F/Ew3UBTdIu6hsW4HC4MVEJygG6TEyJI/lg==} engines: {node: '>=14'} @@ -5075,12 +5055,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/resources@1.22.0': - resolution: {integrity: sha512-+vNeIFPH2hfcNL0AJk/ykJXoUCtR1YaDUZM+p3wZNU4Hq98gzq+7b43xbkXjadD9VhWIUQqEwXyY64q6msPj6A==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' - '@opentelemetry/resources@1.27.0': resolution: {integrity: sha512-jOwt2VJ/lUD5BLc+PMNymDrUCpm5PKi1E9oSVYAvz01U/VdndGmrtV3DU1pG4AwlYhJRHbHfOUIlpBeXCPw6QQ==} engines: {node: '>=14'} @@ -5105,12 +5079,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-base@1.22.0': - resolution: {integrity: sha512-pfTuSIpCKONC6vkTpv6VmACxD+P1woZf4q0K46nSUvXFvOFqjBYKFaAMkKD3M1mlKUUh0Oajwj35qNjMl80m1Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' - '@opentelemetry/sdk-trace-base@1.27.0': resolution: {integrity: sha512-btz6XTQzwsyJjombpeqCX6LhiMQYpzt2pIYNPnw0IPO/3AhT6yjnf8Mnv3ZC2A4eRYOjqrg+bfaXg9XHDRJDWQ==} engines: {node: '>=14'} @@ -5123,10 +5091,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/semantic-conventions@1.22.0': - resolution: {integrity: sha512-CAOgFOKLybd02uj/GhCdEeeBjOS0yeoDeo/CA7ASBSmenpZHAKGB3iDm/rv3BQLcabb/OprDEsSQ1y0P8A7Siw==} - engines: {node: '>=14'} - '@opentelemetry/semantic-conventions@1.27.0': resolution: {integrity: sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==} engines: {node: '>=14'} @@ -5151,32 +5115,34 @@ packages: '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - '@prisma/client@5.11.0': - resolution: {integrity: sha512-SWshvS5FDXvgJKM/a0y9nDC1rqd7KG0Q6ZVzd+U7ZXK5soe73DJxJJgbNBt2GNXOa+ysWB4suTpdK5zfFPhwiw==} - engines: {node: '>=16.13'} + '@prisma/client@6.2.1': + resolution: {integrity: sha512-msKY2iRLISN8t5X0Tj7hU0UWet1u0KuxSPHWuf3IRkB4J95mCvGpyQBfQ6ufcmvKNOMQSq90O2iUmJEN2e5fiA==} + engines: {node: '>=18.18'} peerDependencies: prisma: '*' peerDependenciesMeta: prisma: optional: true - '@prisma/debug@5.11.0': - resolution: {integrity: sha512-N6yYr3AbQqaiUg+OgjkdPp3KPW1vMTAgtKX6+BiB/qB2i1TjLYCrweKcUjzOoRM5BriA4idrkTej9A9QqTfl3A==} + '@prisma/debug@6.2.1': + resolution: {integrity: sha512-0KItvt39CmQxWkEw6oW+RQMD6RZ43SJWgEUnzxN8VC9ixMysa7MzZCZf22LCK5DSooiLNf8vM3LHZm/I/Ni7bQ==} - '@prisma/engines-version@5.11.0-15.efd2449663b3d73d637ea1fd226bafbcf45b3102': - resolution: {integrity: sha512-WXCuyoymvrS4zLz4wQagSsc3/nE6CHy8znyiMv8RKazKymOMd5o9FP5RGwGHAtgoxd+aB/BWqxuP/Ckfu7/3MA==} + '@prisma/engines-version@6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69': + resolution: {integrity: sha512-7tw1qs/9GWSX6qbZs4He09TOTg1ff3gYsB3ubaVNN0Pp1zLm9NC5C5MZShtkz7TyQjx7blhpknB7HwEhlG+PrQ==} - '@prisma/engines@5.11.0': - resolution: {integrity: sha512-gbrpQoBTYWXDRqD+iTYMirDlF9MMlQdxskQXbhARhG6A/uFQjB7DZMYocMQLoiZXO/IskfDOZpPoZE8TBQKtEw==} + '@prisma/engines@6.2.1': + resolution: {integrity: sha512-lTBNLJBCxVT9iP5I7Mn6GlwqAxTpS5qMERrhebkUhtXpGVkBNd/jHnNJBZQW4kGDCKaQg/r2vlJYkzOHnAb7ZQ==} - '@prisma/fetch-engine@5.11.0': - resolution: {integrity: sha512-994viazmHTJ1ymzvWugXod7dZ42T2ROeFuH6zHPcUfp/69+6cl5r9u3NFb6bW8lLdNjwLYEVPeu3hWzxpZeC0w==} + '@prisma/fetch-engine@6.2.1': + resolution: {integrity: sha512-OO7O9d6Mrx2F9i+Gu1LW+DGXXyUFkP7OE5aj9iBfA/2jjDXEJjqa9X0ZmM9NZNo8Uo7ql6zKm6yjDcbAcRrw1A==} - '@prisma/get-platform@5.11.0': - resolution: {integrity: sha512-rxtHpMLxNTHxqWuGOLzR2QOyQi79rK1u1XYAVLZxDGTLz/A+uoDnjz9veBFlicrpWjwuieM4N6jcnjj/DDoidw==} + '@prisma/get-platform@6.2.1': + resolution: {integrity: sha512-zp53yvroPl5m5/gXYLz7tGCNG33bhG+JYCm74ohxOq1pPnrL47VQYFfF3RbTZ7TzGWCrR3EtoiYMywUBw7UK6Q==} - '@prisma/instrumentation@5.11.0': - resolution: {integrity: sha512-ou4nvDpNEY6+t3Dn9juOTz6tK33D0Y4XXkEZ2uPd8KH6Mqmc+4LYOOm470DP7noj7dyJjuGiM+wpPk//HKrcDg==} + '@prisma/instrumentation@6.2.1': + resolution: {integrity: sha512-QrcWRAwNHXX4nHXB+Q94nfm701gPQsR4zkaxYV6qCiENopRi8yYvXt6FNIvxbuwEiWW5Zid6DoWwIsBKJ/5r5w==} + peerDependencies: + '@opentelemetry/api': ^1.8 '@probe.gl/env@3.6.0': resolution: {integrity: sha512-4tTZYUg/8BICC3Yyb9rOeoKeijKbZHRXBEKObrfPmX4sQmYB15ZOUpoVBhAyJkOYVAM8EkPci6Uw5dLCwx2BEQ==} @@ -11565,9 +11531,6 @@ packages: import-in-the-middle@1.11.3: resolution: {integrity: sha512-tNpKEb4AjZrCyrxi+Eyu43h5ig0O8ZRFSXPHh/00/o+4P4pKzVEW/m5lsVtsAT7fCIgmQOAPjdqecGDsBXRxsw==} - import-in-the-middle@1.7.1: - resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} - import-meta-resolve@4.1.0: resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} @@ -14555,9 +14518,9 @@ packages: resolution: {integrity: sha512-DBS3Nir18YtKc8loYCCOGitmiaQ0vTdahPoiXxwNweJDpmVZo+w3tppufOhoK0m8skpRxT56llYLs3VrORnmNQ==} engines: {node: '>=14'} - prisma@5.11.0: - resolution: {integrity: sha512-KCLiug2cs0Je7kGkQBN9jDWoZ90ogE/kvZTUTgz2h94FEo8pczCkPH7fPNXkD1sGU7Yh65risGGD1HQ5DF3r3g==} - engines: {node: '>=16.13'} + prisma@6.2.1: + resolution: {integrity: sha512-hhyM0H13pQleQ+br4CkzGizS5I0oInoeTw3JfLw1BRZduBSQxPILlJLwi+46wZzj9Je7ndyQEMGw/n5cN2fknA==} + engines: {node: '>=18.18'} hasBin: true prismjs@1.27.0: @@ -20706,7 +20669,7 @@ snapshots: class-transformer: 0.5.1 class-validator: 0.14.1 - '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.2(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1))(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(@nestjs/core@10.3.5)(@prisma/client@5.11.0(prisma@5.11.0))(reflect-metadata@0.2.1)(rxjs@7.8.1)': + '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.2(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1))(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(@nestjs/core@10.3.5)(@prisma/client@6.2.1(prisma@6.2.1))(reflect-metadata@0.2.1)(rxjs@7.8.1)': dependencies: '@nestjs/common': 10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) '@nestjs/core': 10.3.5(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(@nestjs/platform-express@10.3.5)(@nestjs/websockets@10.3.5)(encoding@0.1.13)(reflect-metadata@0.2.1)(rxjs@7.8.1) @@ -20718,7 +20681,7 @@ snapshots: '@grpc/grpc-js': 1.12.4 '@grpc/proto-loader': 0.7.13 '@nestjs/axios': 3.0.2(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1) - '@prisma/client': 5.11.0(prisma@5.11.0) + '@prisma/client': 6.2.1(prisma@6.2.1) '@nestjs/testing@10.3.5(@nestjs/common@10.3.5(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1))(@nestjs/core@10.3.5)(@nestjs/platform-express@10.3.5)': dependencies: @@ -20864,27 +20827,16 @@ snapshots: '@one-ini/wasm@0.1.1': {} - '@opentelemetry/api-logs@0.49.1': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs@0.54.2': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api@1.8.0': {} - '@opentelemetry/api@1.9.0': {} '@opentelemetry/context-async-hooks@1.27.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core@1.22.0(@opentelemetry/api@1.8.0)': - dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/semantic-conventions': 1.22.0 - '@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -20989,18 +20941,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.49.1(@opentelemetry/api@1.8.0)': - dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/api-logs': 0.49.1 - '@types/shimmer': 1.2.0 - import-in-the-middle: 1.7.1 - require-in-the-middle: 7.4.0 - semver: 7.6.3 - shimmer: 1.2.1 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -21048,12 +20988,6 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources@1.22.0(@opentelemetry/api@1.8.0)': - dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/core': 1.22.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.22.0 - '@opentelemetry/resources@1.27.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -21095,13 +21029,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/sdk-trace-base@1.22.0(@opentelemetry/api@1.8.0)': - dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/core': 1.22.0(@opentelemetry/api@1.8.0) - '@opentelemetry/resources': 1.22.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.22.0 - '@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -21119,8 +21046,6 @@ snapshots: '@opentelemetry/sdk-trace-base': 1.27.0(@opentelemetry/api@1.9.0) semver: 7.6.3 - '@opentelemetry/semantic-conventions@1.22.0': {} - '@opentelemetry/semantic-conventions@1.27.0': {} '@opentelemetry/semantic-conventions@1.28.0': {} @@ -21136,36 +21061,35 @@ snapshots: '@polka/url@1.0.0-next.28': {} - '@prisma/client@5.11.0(prisma@5.11.0)': + '@prisma/client@6.2.1(prisma@6.2.1)': optionalDependencies: - prisma: 5.11.0 + prisma: 6.2.1 - '@prisma/debug@5.11.0': {} + '@prisma/debug@6.2.1': {} - '@prisma/engines-version@5.11.0-15.efd2449663b3d73d637ea1fd226bafbcf45b3102': {} + '@prisma/engines-version@6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69': {} - '@prisma/engines@5.11.0': + '@prisma/engines@6.2.1': dependencies: - '@prisma/debug': 5.11.0 - '@prisma/engines-version': 5.11.0-15.efd2449663b3d73d637ea1fd226bafbcf45b3102 - '@prisma/fetch-engine': 5.11.0 - '@prisma/get-platform': 5.11.0 + '@prisma/debug': 6.2.1 + '@prisma/engines-version': 6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69 + '@prisma/fetch-engine': 6.2.1 + '@prisma/get-platform': 6.2.1 - '@prisma/fetch-engine@5.11.0': + '@prisma/fetch-engine@6.2.1': dependencies: - '@prisma/debug': 5.11.0 - '@prisma/engines-version': 5.11.0-15.efd2449663b3d73d637ea1fd226bafbcf45b3102 - '@prisma/get-platform': 5.11.0 + '@prisma/debug': 6.2.1 + '@prisma/engines-version': 6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69 + '@prisma/get-platform': 6.2.1 - '@prisma/get-platform@5.11.0': + '@prisma/get-platform@6.2.1': dependencies: - '@prisma/debug': 5.11.0 + '@prisma/debug': 6.2.1 - '@prisma/instrumentation@5.11.0': + '@prisma/instrumentation@6.2.1(@opentelemetry/api@1.9.0)': dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/instrumentation': 0.49.1(@opentelemetry/api@1.8.0) - '@opentelemetry/sdk-trace-base': 1.22.0(@opentelemetry/api@1.8.0) + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.54.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -22178,6 +22102,14 @@ snapshots: optionalDependencies: rollup: 2.78.0 + '@rollup/pluginutils@5.1.4(rollup@2.79.2)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 2.79.2 + '@rollup/pluginutils@5.1.4(rollup@4.28.1)': dependencies: '@types/estree': 1.0.6 @@ -27799,7 +27731,7 @@ snapshots: '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.1(eslint@8.57.0) @@ -27822,7 +27754,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 4.4.0 enhanced-resolve: 5.17.1 @@ -27867,7 +27799,7 @@ snapshots: '@typescript-eslint/parser': 7.3.1(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.3.1(eslint@8.57.0)(typescript@5.4.3))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -29395,13 +29327,6 @@ snapshots: cjs-module-lexer: 1.4.1 module-details-from-path: 1.0.3 - import-in-the-middle@1.7.1: - dependencies: - acorn: 8.14.0 - acorn-import-assertions: 1.9.0(acorn@8.14.0) - cjs-module-lexer: 1.4.1 - module-details-from-path: 1.0.3 - import-meta-resolve@4.1.0: {} imurmurhash@0.1.4: {} @@ -33137,9 +33062,11 @@ snapshots: pug: 3.0.3 uuid: 9.0.1 - prisma@5.11.0: + prisma@6.2.1: dependencies: - '@prisma/engines': 5.11.0 + '@prisma/engines': 6.2.1 + optionalDependencies: + fsevents: 2.3.3 prismjs@1.27.0: {} @@ -36176,9 +36103,9 @@ snapshots: - supports-color - typescript - vite-plugin-svgr@4.2.0(rollup@4.28.1)(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)): + vite-plugin-svgr@4.2.0(rollup@2.79.2)(typescript@5.4.3)(vite@5.4.11(@types/node@20.9.0)(terser@5.37.0)): dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.28.1) + '@rollup/pluginutils': 5.1.4(rollup@2.79.2) '@svgr/core': 8.1.0(typescript@5.4.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.4.3)) vite: 5.4.11(@types/node@20.9.0)(terser@5.37.0)