From bd8feb66b8f3585df1d29bf4e970ade1885ef2d3 Mon Sep 17 00:00:00 2001 From: Artem Savchenko Date: Sun, 22 Feb 2026 13:28:58 +0700 Subject: [PATCH] Sync with foundation Signed-off-by: Artem Savchenko --- packages/account-client/package.json | 6 +- packages/account-client/src/client.ts | 21 +++ packages/account-client/src/types.ts | 5 + packages/analytics-service/package.json | 2 +- packages/analytics/package.json | 2 +- packages/api-client/package.json | 6 +- packages/client-resources/package.json | 4 +- packages/client/package.json | 4 +- packages/collaborator-client/package.json | 6 +- packages/core/lang/cs.json | 5 +- packages/core/lang/de.json | 5 +- packages/core/lang/en.json | 5 +- packages/core/lang/es.json | 5 +- packages/core/lang/fr.json | 5 +- packages/core/lang/it.json | 5 +- packages/core/lang/ja.json | 5 +- packages/core/lang/pt-br.json | 68 ++++++++ packages/core/lang/pt.json | 5 +- packages/core/lang/ru.json | 5 +- packages/core/lang/tr.json | 5 +- packages/core/lang/zh.json | 5 +- packages/core/package.json | 4 +- packages/core/src/__tests__/memdb.test.ts | 151 +++++++++++++++++- packages/core/src/__tests__/minmodel.ts | 30 +++- packages/core/src/classes.ts | 13 +- packages/core/src/component.ts | 25 ++- packages/core/src/index.ts | 1 + packages/core/src/memdb.ts | 10 +- packages/core/src/operations.ts | 11 ++ packages/core/src/predicate.ts | 3 + packages/core/src/storage.ts | 9 +- packages/core/src/utils.ts | 2 +- packages/core/src/versioning.ts | 13 ++ packages/hulylake-client/package.json | 6 +- .../src/__tests__/performance.test.ts | 6 +- packages/model/package.json | 4 +- packages/platform/lang/pt-br.json | 31 ++++ packages/platform/package.json | 2 +- packages/postgres-base/package.json | 2 +- packages/query/package.json | 2 +- packages/query/src/index.ts | 115 ++++++++++--- packages/rank/package.json | 2 +- packages/retry/package.json | 4 +- packages/rpc/package.json | 4 +- packages/storage-client/package.json | 6 +- .../storage-client/src/client/datalake.ts | 10 ++ packages/storage-client/src/client/front.ts | 10 ++ .../storage-client/src/client/hulylake.ts | 10 ++ packages/storage-client/src/types.ts | 1 + packages/storage/package.json | 4 +- packages/text-core/package.json | 4 +- packages/text-html/package.json | 2 +- packages/text-markdown/package.json | 2 +- packages/text-ydoc/package.json | 4 +- packages/text/package.json | 2 +- packages/token/package.json | 4 +- 56 files changed, 594 insertions(+), 94 deletions(-) create mode 100644 packages/core/lang/pt-br.json create mode 100644 packages/core/src/versioning.ts create mode 100644 packages/platform/lang/pt-br.json diff --git a/packages/account-client/package.json b/packages/account-client/package.json index c2efc55acd..41037dd81c 100644 --- a/packages/account-client/package.json +++ b/packages/account-client/package.json @@ -26,16 +26,16 @@ "devDependencies": { "cross-env": "~7.0.3", "@hcengineering/platform-rig": "^0.7.19", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "@typescript-eslint/eslint-plugin": "^6.21.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", - "esbuild": "^0.25.9", + "esbuild": "^0.25.10", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/account-client/src/client.ts b/packages/account-client/src/client.ts index a974028cf3..14a7379ac8 100644 --- a/packages/account-client/src/client.ts +++ b/packages/account-client/src/client.ts @@ -42,6 +42,7 @@ import type { LoginInfo, LoginInfoByToken, LoginInfoRequestData, + InviteInfo, LoginInfoWithWorkspaces, MailboxInfo, MailboxOptions, @@ -124,7 +125,9 @@ export interface AccountClient { } ) => Promise checkJoin: (inviteId: string) => Promise + joinByToken: (inviteId: string) => Promise checkAutoJoin: (inviteId: string, firstName?: string, lastName?: string) => Promise + getInviteInfo: (inviteId: string) => Promise getWorkspaceInfo: (updateLastVisit?: boolean) => Promise getWorkspacesInfo: (workspaces: WorkspaceUuid[]) => Promise updateLastVisit: (workspaces: WorkspaceUuid[]) => Promise @@ -577,6 +580,15 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } + async joinByToken (inviteId: string): Promise { + const request = { + method: 'joinByToken' as const, + params: { inviteId } + } + + return await this.rpc(request) + } + async checkAutoJoin (inviteId: string, firstName?: string, lastName?: string): Promise { const request = { method: 'checkAutoJoin' as const, @@ -586,6 +598,15 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } + async getInviteInfo (inviteId: string): Promise { + const request = { + method: 'getInviteInfo' as const, + params: { inviteId } + } + + return await this.rpc(request) + } + async getWorkspacesInfo (ids: WorkspaceUuid[]): Promise { const request = { method: 'getWorkspacesInfo' as const, diff --git a/packages/account-client/src/types.ts b/packages/account-client/src/types.ts index a267ae06b9..f055a74d70 100644 --- a/packages/account-client/src/types.ts +++ b/packages/account-client/src/types.ts @@ -80,6 +80,11 @@ export interface WorkspaceInviteInfo { name?: string } +/** Public invite details from getInviteInfo (no auth required). */ +export interface InviteInfo { + workspaceName: string | null +} + export interface OtpInfo { sent: boolean retryOn: Timestamp diff --git a/packages/analytics-service/package.json b/packages/analytics-service/package.json index 1d962bd901..59e1dd2d57 100644 --- a/packages/analytics-service/package.json +++ b/packages/analytics-service/package.json @@ -32,7 +32,7 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 72c9cce27b..02b6ac81d1 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -32,7 +32,7 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/api-client/package.json b/packages/api-client/package.json index 31d91bd206..9219ffb9b0 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -40,12 +40,12 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", - "ts-node": "^10.8.0", - "@types/node": "^22.15.29", + "ts-node": "^10.9.2", + "@types/node": "^22.18.1", "@types/jest": "^29.5.5", "@types/ws": "^8.5.12", "@types/snappyjs": "^0.7.1", diff --git a/packages/client-resources/package.json b/packages/client-resources/package.json index cb27e6059d..ee974b2220 100644 --- a/packages/client-resources/package.json +++ b/packages/client-resources/package.json @@ -32,13 +32,13 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", "@types/snappyjs": "^0.7.1", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "ws": "^8.18.2", "@types/ws": "^8.5.12", "eslint-plugin-svelte": "^2.35.1" diff --git a/packages/client/package.json b/packages/client/package.json index 8e733de90a..541e6efdb8 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -32,12 +32,12 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "eslint-plugin-svelte": "^2.35.1" }, "dependencies": { diff --git a/packages/collaborator-client/package.json b/packages/collaborator-client/package.json index 161be0611e..3c8a7c3644 100644 --- a/packages/collaborator-client/package.json +++ b/packages/collaborator-client/package.json @@ -26,16 +26,16 @@ "devDependencies": { "cross-env": "~7.0.3", "@hcengineering/platform-rig": "^0.7.19", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "@typescript-eslint/eslint-plugin": "^6.21.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", - "esbuild": "^0.25.9", + "esbuild": "^0.25.10", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/core/lang/cs.json b/packages/core/lang/cs.json index 8a312b656e..f9fa410241 100644 --- a/packages/core/lang/cs.json +++ b/packages/core/lang/cs.json @@ -61,12 +61,15 @@ "ArchiveSpaceDescription": "Umožňuje uživatelům archivovat prostor", "AutoJoin": "Automatické připojení", "AutoJoinDescr": "Automaticky připojit nové zaměstnance k tomuto prostoru", + "RBAC": "Řízení přístupu na základě rolí", + "RBACDescr": "Vyžadovat řízení přístupu na základě rolí pro provádění akcí v tomto prostoru", "BlobSize": "Velikost", "BlobContentType": "Typ obsahu", "Relation": "Vztah", "Relations": "Vztahy", "AddRelation": "Přidat vztah", "PersonId": "Osoba", - "AccountId": "Účet" + "AccountId": "Účet", + "Version": "Verze" } } diff --git a/packages/core/lang/de.json b/packages/core/lang/de.json index bc8768fa84..803b5b8098 100644 --- a/packages/core/lang/de.json +++ b/packages/core/lang/de.json @@ -61,12 +61,15 @@ "ArchiveSpaceDescription": "Gewährt Benutzern die Möglichkeit, den Arbeitsbereich zu archivieren", "AutoJoin": "Automatisch beitreten", "AutoJoinDescr": "Neue Mitarbeiter automatisch diesem Arbeitsbereich hinzufügen", + "RBAC": "Rollenbasierte Zugriffskontrolle", + "RBACDescr": "Erfordert rollenbasierten Zugriff, um Aktionen in diesem Arbeitsbereich auszuführen", "BlobSize": "Größe", "BlobContentType": "Inhaltstyp", "Relation": "Beziehung", "Relations": "Beziehungen", "AddRelation": "Beziehung hinzufügen", "PersonId": "Person", - "AccountId": "Konto" + "AccountId": "Konto", + "Version": "Version" } } diff --git a/packages/core/lang/en.json b/packages/core/lang/en.json index f9687e3159..6216e771fe 100644 --- a/packages/core/lang/en.json +++ b/packages/core/lang/en.json @@ -61,12 +61,15 @@ "ArchiveSpaceDescription": "Grants users ability to archive the space", "AutoJoin": "Auto join", "AutoJoinDescr": "Automatically join new employees to this space", + "RBAC": "Role-based access control", + "RBACDescr": "Require role-based access to perform actions in this space", "BlobSize": "Size", "BlobContentType": "Content type", "Relation": "Relation", "Relations": "Relations", "AddRelation": "Add relation", "PersonId": "Person", - "AccountId": "Account" + "AccountId": "Account", + "Version": "Version" } } diff --git a/packages/core/lang/es.json b/packages/core/lang/es.json index e6917fa281..7842f2fa8b 100644 --- a/packages/core/lang/es.json +++ b/packages/core/lang/es.json @@ -54,12 +54,15 @@ "ArchiveSpaceDescription": "Concede a los usuarios la capacidad de archivar el espacio", "AutoJoin": "Auto unirse", "AutoJoinDescr": "Unirse automáticamente a los nuevos empleados a este espacio", + "RBAC": "Control de acceso basado en roles", + "RBACDescr": "Requerir acceso basado en roles para realizar acciones en este espacio", "BlobSize": "Tamaño", "BlobContentType": "Tipo de contenido", "Relation": "Relación", "Relations": "Relaciones", "AddRelation": "Añadir relación", "PersonId": "Id. de persona", - "AccountId": "Cuenta" + "AccountId": "Cuenta", + "Version": "Versión" } } diff --git a/packages/core/lang/fr.json b/packages/core/lang/fr.json index bb4b311b71..d344db529e 100644 --- a/packages/core/lang/fr.json +++ b/packages/core/lang/fr.json @@ -61,12 +61,15 @@ "ArchiveSpaceDescription": "Accorde aux utilisateurs la capacité d'archiver l'espace", "AutoJoin": "Rejoindre automatiquement", "AutoJoinDescr": "Ajouter automatiquement les nouveaux employés à cet espace", + "RBAC": "Contrôle d'accès basé sur les rôles", + "RBACDescr": "Exiger un accès basé sur les rôles pour effectuer des actions dans cet espace", "BlobSize": "Taille", "BlobContentType": "Type de contenu", "Relation": "Relation", "Relations": "Relations", "AddRelation": "Ajouter une relation", "PersonId": "Id de personne", - "AccountId": "Compte" + "AccountId": "Compte", + "Version": "Version" } } diff --git a/packages/core/lang/it.json b/packages/core/lang/it.json index 68f072205e..cb491afd14 100644 --- a/packages/core/lang/it.json +++ b/packages/core/lang/it.json @@ -61,12 +61,15 @@ "ArchiveSpaceDescription": "Concede agli utenti la possibilità di archiviare lo spazio", "AutoJoin": "Partecipazione automatica", "AutoJoinDescr": "Aggiungi automaticamente i nuovi dipendenti a questo spazio", + "RBAC": "Controllo accessi basato sui ruoli", + "RBACDescr": "Richiedi accesso basato sui ruoli per eseguire azioni in questo spazio", "BlobSize": "Dimensione", "BlobContentType": "Tipo di contenuto", "Relation": "Relazione", "Relations": "Relazioni", "AddRelation": "Aggiungi relazione", "PersonId": "ID persona", - "AccountId": "ID account" + "AccountId": "ID account", + "Version": "Versione" } } diff --git a/packages/core/lang/ja.json b/packages/core/lang/ja.json index cdb6c0daa1..4d9b5e848d 100644 --- a/packages/core/lang/ja.json +++ b/packages/core/lang/ja.json @@ -61,12 +61,15 @@ "ArchiveSpaceDescription": "ユーザーにスペースをアーカイブする権限を付与します", "AutoJoin": "自動参加", "AutoJoinDescr": "新しいユーザーを自動的にこのスペースに参加させます", + "RBAC": "ロールベースアクセス制御", + "RBACDescr": "このスペースでの操作を実行するためにロールベースのアクセスを要求します", "BlobSize": "サイズ", "BlobContentType": "コンテンツタイプ", "Relation": "関係", "Relations": "関係一覧", "AddRelation": "関係を追加", "PersonId": "人物", - "AccountId": "アカウント" + "AccountId": "アカウント", + "Version": "バージョン" } } diff --git a/packages/core/lang/pt-br.json b/packages/core/lang/pt-br.json new file mode 100644 index 0000000000..c128d88b0c --- /dev/null +++ b/packages/core/lang/pt-br.json @@ -0,0 +1,68 @@ +{ + "string": { + "Id": "Id", + "Space": "Espaço", + "Spaces": "Espaços", + "SpacesDescription": "Gerenciamento do tipo de espaço para todos os espaços", + "Modified": "Modificado", + "ModifiedDate": "Data de modificação", + "ModifiedBy": "Modificado por", + "Class": "Classe", + "AttachedTo": "Anexado à", + "AttachedToClass": "Classe anexada", + "Name": "Nome", + "Description": "Descrição", + "Private": "Privado", + "Archived": "Arquivado", + "ClassLabel": "Tipo", + "ClassPropertyLabel": "Legenda", + "String": "Texto", + "Markup": "Marcação", + "Number": "Número", + "Boolean": "Caixa de seleção", + "Timestamp": "Marca de data/hora", + "Date": "Data", + "IntlString": "Texto internacional", + "Ref": "Referência", + "Collection": "Coleção", + "Array": "Seleção múltipla", + "Enum": "Seleção", + "Members": "Membros", + "Hyperlink": "URL", + "MarkupBlobRef": "Colaborativo", + "Object": "Objeto", + "System": "Sistema", + "CreatedBy": "Criado por", + "CreatedDate": "Data de criação", + "Status": "Estado", + "StatusCategory": "Categoria do estado", + "Account": "Conta", + "Rank": "Ranking", + "Owners": "Proprietários", + "Permission": "Permissão", + "CreateObject": "Criar objeto", + "UpdateObject": "Atualizar objeto", + "DeleteObject": "Apagar objeto", + "ForbidDeleteObject": "Proibir apagar objeto", + "UpdateSpace": "Atualizar espaço", + "ArchiveSpace": "Arquivar espaço", + "CreateObjectDescription": "Concede aos usuários a capacidade de criar objetos no espaço", + "UpdateObjectDescription": "Concede aos usuários a capacidade de atualizar objetos no espaço", + "DeleteObjectDescription": "Concede aos usuários a capacidade de apagar objetos no espaço", + "ForbidDeleteObjectDescription": "Proíbe aos usuários a capacidade de apagar objetos no espaço", + "UpdateSpaceDescription": "Concede aos usuários a capacidade de atualizar o espaço", + "ArchiveSpaceDescription": "Concede aos usuários a capacidade de arquivar o espaço", + "AutoJoin": "Auto adesão", + "AutoJoinDescr": "Adesão automática de novos funcionários a este espaço", + "RBAC": "Controle de acesso baseado em funções", + "RBACDescr": "Exigir acesso baseado em funções para executar ações neste espaço", + "BlobSize": "Tamanho", + "BlobContentType": "Tipo de conteúdo", + "Relation": "Relação", + "Relations": "Relações", + "AddRelation": "Adicionar relação", + "PersonId": "ID de pessoa", + "AccountId": "Conta", + "Version": "Versão" + } +} diff --git a/packages/core/lang/pt.json b/packages/core/lang/pt.json index c7df192c7d..9ae51b2c3e 100644 --- a/packages/core/lang/pt.json +++ b/packages/core/lang/pt.json @@ -54,12 +54,15 @@ "ArchiveSpaceDescription": "Concede aos usuários a capacidade de arquivar o espaço", "AutoJoin": "Auto adesão", "AutoJoinDescr": "Adesão automática de novos funcionários a este espaço", + "RBAC": "Controle de acesso baseado em funções", + "RBACDescr": "Exigir acesso baseado em funções para executar ações neste espaço", "BlobSize": "Tamanho", "BlobContentType": "Tipo de conteúdo", "Relation": "Relação", "Relations": "Relações", "AddRelation": "Adicionar relação", "PersonId": "ID de pessoa", - "AccountId": "Conta" + "AccountId": "Conta", + "Version": "Versão" } } diff --git a/packages/core/lang/ru.json b/packages/core/lang/ru.json index 24cb255c36..8bb9bfa12b 100644 --- a/packages/core/lang/ru.json +++ b/packages/core/lang/ru.json @@ -61,12 +61,15 @@ "ArchiveSpaceDescription": "Дает пользователям разрешение архивировать пространство", "AutoJoin": "Автоприсоединение", "AutoJoinDescr": "Автоматически присоединять новых сотрудников к этому пространству", + "RBAC": "Ролевой доступ", + "RBACDescr": "Требовать ролевой доступ для выполнения действий в этом пространстве", "BlobSize": "Размер", "BlobContentType": "Тип контента", "Relation": "Связь", "Relations": "Связи", "AddRelation": "Добавить связь", "PersonId": "Персона", - "AccountId": "Аккаунт" + "AccountId": "Аккаунт", + "Version": "Версия" } } diff --git a/packages/core/lang/tr.json b/packages/core/lang/tr.json index 1f862d2e08..2595a1a0f9 100644 --- a/packages/core/lang/tr.json +++ b/packages/core/lang/tr.json @@ -61,12 +61,15 @@ "ArchiveSpaceDescription": "Kullanıcılara alanı arşivleme yetkisi verir", "AutoJoin": "Otomatik katıl", "AutoJoinDescr": "Yeni çalışanları bu alana otomatik olarak ekle", + "RBAC": "Rol tabanlı erişim kontrolü", + "RBACDescr": "Bu alanda işlemleri gerçekleştirmek için rol tabanlı erişim gerektir", "BlobSize": "Boyut", "BlobContentType": "İçerik türü", "Relation": "İlişki", "Relations": "İlişkiler", "AddRelation": "İlişki ekle", "PersonId": "Kişi", - "AccountId": "Hesap" + "AccountId": "Hesap", + "Version": "Sürüm" } } diff --git a/packages/core/lang/zh.json b/packages/core/lang/zh.json index 696a334f60..3fd492d867 100644 --- a/packages/core/lang/zh.json +++ b/packages/core/lang/zh.json @@ -61,12 +61,15 @@ "ArchiveSpaceDescription": "授予用户归档空间的权限", "AutoJoin": "自动加入", "AutoJoinDescr": "自动将新员工加入此空间", + "RBAC": "基于角色的访问控制", + "RBACDescr": "要求基于角色的访问权限以在此空间中执行操作", "BlobSize": "大小", "BlobContentType": "內容類型", "Relation": "关系", "Relations": "关系", "AddRelation": "添加关系", "PersonId": "人员 ID", - "AccountId": "帐户" + "AccountId": "帐户", + "Version": "版本" } } diff --git a/packages/core/package.json b/packages/core/package.json index 5b4fa2abba..85995b515a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -36,12 +36,12 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "eslint-plugin-svelte": "^2.35.1" }, "dependencies": { diff --git a/packages/core/src/__tests__/memdb.test.ts b/packages/core/src/__tests__/memdb.test.ts index 9d4f3b1717..ae17e9ef2c 100644 --- a/packages/core/src/__tests__/memdb.test.ts +++ b/packages/core/src/__tests__/memdb.test.ts @@ -14,7 +14,7 @@ // import { type Client, type DomainParams, type DomainRequestOptions, type DomainResult } from '..' -import type { Class, Doc, Obj, OperationDomain, Ref } from '../classes' +import type { Class, Doc, Obj, OperationDomain, Ref, Space } from '../classes' import core from '../component' import { Hierarchy } from '../hierarchy' import { ModelDb, TxDb } from '../memdb' @@ -293,6 +293,155 @@ describe('memdb', () => { expect(result2).toHaveLength(1) }) + it('check associations', async () => { + const { model } = await createModel() + const operations = new TxOperations(model, core.account.System) + const association = await operations.findOne(core.class.Association, {}) + if (association == null) { + throw new Error('Association not found') + } + + const spaces = await operations.findAll(core.class.Space, {}) + expect(spaces).toHaveLength(2) + + const first = await operations.addCollection( + test.class.TestComment, + core.space.Model, + spaces[0]._id, + spaces[0]._class, + 'comments', + { + message: 'msg' + } + ) + + const second = await operations.addCollection( + test.class.TestComment, + core.space.Model, + first, + test.class.TestComment, + 'comments', + { + message: 'msg2' + } + ) + + await operations.createDoc(core.class.Relation, '' as Ref, { + docA: first, + docB: second, + association: association._id + }) + + const r = await operations.findAll( + test.class.TestComment, + { _id: first }, + { + associations: [[association._id, 1]] + } + ) + expect(r.length).toEqual(1) + expect((r[0].$associations?.[association._id + '_b'][0] as any)?._id).toEqual(second) + }) + + it('check deep associations', async () => { + const { model } = await createModel() + const operations = new TxOperations(model, core.account.System) + const association = await operations.findOne(core.class.Association, {}) + if (association == null) { + throw new Error('Association not found') + } + + const spaces = await operations.findAll(core.class.Space, {}) + expect(spaces).toHaveLength(2) + + const zero = await operations.addCollection( + test.class.TestComment, + core.space.Model, + spaces[0]._id, + spaces[0]._class, + 'comments', + { + message: 'msg' + } + ) + + const first = await operations.addCollection( + test.class.TestComment, + core.space.Model, + spaces[0]._id, + spaces[0]._class, + 'comments', + { + message: 'msg' + } + ) + + const second = await operations.addCollection( + test.class.TestComment, + core.space.Model, + first, + test.class.TestComment, + 'comments', + { + message: 'msg2' + } + ) + + const second2 = await operations.addCollection( + test.class.TestComment, + core.space.Model, + first, + test.class.TestComment, + 'comments', + { + message: 'msg2' + } + ) + + const third = await operations.addCollection( + test.class.TestComment, + core.space.Model, + spaces[0]._id, + spaces[0]._class, + 'comments', + { + message: 'msg3' + } + ) + await operations.createDoc(core.class.Relation, '' as Ref, { + docA: first, + docB: second, + association: association._id + }) + + await operations.createDoc(core.class.Relation, '' as Ref, { + docA: first, + docB: second2, + association: association._id + }) + + await operations.createDoc(core.class.Relation, '' as Ref, { + docA: second, + docB: third, + association: association._id + }) + + const r = await operations.findAll( + test.class.TestComment, + { _id: { $in: [zero, first] } }, + { + associations: [[association._id, 1, [[association._id, 1]]]] + } + ) + expect(r.length).toEqual(2) + expect(r[1].$associations?.[`${association._id}_b`]).toHaveLength(2) + expect((r[1].$associations?.[`${association._id}_b`][0] as any)?._id).toEqual(second) + expect(r[1].$associations?.[`${association._id}_b`][1]?.$associations?.[`${association._id}_b`]).toHaveLength(0) + expect( + (r[1].$associations?.[`${association._id}_b`][0]?.$associations?.[`${association._id}_b`][0] as any)?._id + ).toEqual(third) + }) + it('lookups', async () => { const { model } = await createModel() diff --git a/packages/core/src/__tests__/minmodel.ts b/packages/core/src/__tests__/minmodel.ts index cf9f493126..de03e2a8ac 100644 --- a/packages/core/src/__tests__/minmodel.ts +++ b/packages/core/src/__tests__/minmodel.ts @@ -16,7 +16,7 @@ import type { IntlString, Plugin } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' import type { Arr, AttachedDoc, Class, Data, Doc, Interface, Mixin, Obj, Ref, Space } from '../classes' -import { ClassifierKind, DOMAIN_MODEL } from '../classes' +import { ClassifierKind, DOMAIN_MODEL, DOMAIN_RELATION } from '../classes' import core from '../component' import type { DocumentUpdate, TxCUD, TxCreateDoc, TxRemoveDoc, TxUpdateDoc } from '../tx' import { DOMAIN_TX, TxFactory } from '../tx' @@ -252,6 +252,34 @@ export function genMinModel (): TxCUD[] { }) ) + txes.push( + createClass(core.class.Relation, { + label: 'Relation' as IntlString, + extends: core.class.Doc, + kind: ClassifierKind.CLASS, + domain: DOMAIN_RELATION + }) + ) + + txes.push( + createClass(core.class.Association, { + label: 'Association' as IntlString, + extends: core.class.Doc, + kind: ClassifierKind.CLASS, + domain: DOMAIN_MODEL + }) + ) + + txes.push( + createDoc(core.class.Association, { + nameA: 'my-assoc', + nameB: 'my-assoc', + classA: test.class.TestComment, + classB: test.class.TestComment, + type: '1:1' + }) + ) + txes.push( createDoc(core.class.Space, { name: 'Sp1', diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index 555d9dc817..4307dbf502 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -15,9 +15,9 @@ // import type { Asset, IntlString, Plugin } from '@hcengineering/platform' +import { Tx } from '.' import type { DocumentQuery } from './storage' import { type WorkspaceDataId, type WorkspaceUuid } from './utils' -import { Tx } from '.' /** * @public @@ -490,6 +490,7 @@ export interface SystemSpace extends Space {} */ export interface TypedSpace extends Space { type: Ref + restricted?: boolean // if true user must have permission to any txes } /** @@ -551,6 +552,14 @@ export interface Permission extends Doc { icon?: Asset } +export interface AttributePermission extends Permission { + attribute: Ref +} + +export interface ClassPermission extends Permission { + targetClass: Ref> +} + /** * @public */ @@ -935,8 +944,10 @@ export type SocialKey = Pick export interface ClassCollaborators extends Doc { attachedTo: Ref> + allFields?: boolean // for all (PersonId | Ref | PersonId[] | Ref[]) attributes fields: (keyof T)[] // PersonId | Ref | PersonId[] | Ref[] provideSecurity?: boolean // If true, will provide security for collaborators + provideAttachedSecurity?: boolean // If true, will provide security for collaborators of attached doc } export interface Collaborator extends AttachedDoc { diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 441c27ed75..e64793c5c5 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -15,18 +15,23 @@ import type { Asset, IntlString, Metadata, Plugin, StatusCode } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' import type { BenchmarkDoc } from './benchmark' -import { AccountRole, TxAccessLevel } from './classes' import type { Account, + AccountUuid, AnyAttribute, ArrOf, Association, AttachedDoc, + AttributePermission, Blob, Class, + ClassCollaborators, + ClassPermission, + Collaborator, Collection, Configuration, ConfigurationElement, + CustomSequence, Doc, DomainIndexConfiguration, Enum, @@ -49,7 +54,6 @@ import type { Relation, Role, Sequence, - CustomSequence, Space, SpaceType, SpaceTypeDescriptor, @@ -60,11 +64,9 @@ import type { TypeAny, TypedSpace, UserStatus, - Version, - AccountUuid, - ClassCollaborators, - Collaborator + Version } from './classes' +import { AccountRole, TxAccessLevel } from './classes' import { type Status, type StatusCategory } from './status' import type { Tx, @@ -78,6 +80,7 @@ import type { TxUpdateDoc, TxWorkspaceEvent } from './tx' +import { VersionableClass } from './versioning' /** * @public @@ -129,6 +132,8 @@ export default plugin(coreId, { SpaceType: '' as Ref>, Role: '' as Ref>, Permission: '' as Ref>, + AttributePermission: '' as Ref>, + ClassPermission: '' as Ref>, Type: '' as Ref>>, TypeRelation: '' as Ref>>, TypeString: '' as Ref>>, @@ -195,7 +200,8 @@ export default plugin(coreId, { IndexConfiguration: '' as Ref>>, SpacesTypeData: '' as Ref>, TransientConfiguration: '' as Ref>, - TxAccessLevel: '' as Ref> + TxAccessLevel: '' as Ref>, + VersionableClass: '' as Ref> }, space: { Tx: '' as Ref, @@ -287,7 +293,10 @@ export default plugin(coreId, { UpdateSpaceDescription: '' as IntlString, ArchiveSpaceDescription: '' as IntlString, AutoJoin: '' as IntlString, - AutoJoinDescr: '' as IntlString + AutoJoinDescr: '' as IntlString, + RBAC: '' as IntlString, + RBACDescr: '' as IntlString, + Version: '' as IntlString }, descriptor: { SpacesType: '' as Ref diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5cc09fb86b..de5ad39f63 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -43,5 +43,6 @@ export * from './common' export * from './time' export * from './benchmark' export * from './collaborators' +export * from './versioning' export default core diff --git a/packages/core/src/memdb.ts b/packages/core/src/memdb.ts index 9feddf0201..8e07c32266 100644 --- a/packages/core/src/memdb.ts +++ b/packages/core/src/memdb.ts @@ -159,7 +159,7 @@ export abstract class MemDb extends TxProcessor implements Storage { private async getAssociationValue( doc: T, associations: AssociationQuery[] - ): Promise> { + ): Promise[]>> { const result: Record = {} for (const association of associations) { const _id = association[0] @@ -170,8 +170,11 @@ export abstract class MemDb extends TxProcessor implements Storage { const key2 = !isReverse ? 'docB' : 'docA' const _class = !isReverse ? assoc.classB : assoc.classA const relations = await this.findAll(core.class.Relation, { association: _id, [key]: doc._id }) - const objects = await this.findAll(_class, { _id: { $in: relations.map((r) => r[key2]) } }) - result[_id] = objects + let objects = await this.findAll(_class, { _id: { $in: relations.map((r) => r[key2]) } }) + if (association[2] !== undefined) { + objects = toFindResult(await this.fillAssociations(objects, association[2]), objects.length) + } + result[`${_id}_${!isReverse ? 'b' : 'a'}`] = objects } return result } @@ -246,6 +249,7 @@ export abstract class MemDb extends TxProcessor implements Storage { // We need to filter instances without mixin was set result = result.filter((r) => (r as any)[_class] !== undefined) } + if (options?.sort !== undefined) resultSort(result, options?.sort, _class, this.hierarchy, this) const total = result.length result = result.slice(0, options?.limit) diff --git a/packages/core/src/operations.ts b/packages/core/src/operations.ts index 8a182e5514..8e64f87928 100644 --- a/packages/core/src/operations.ts +++ b/packages/core/src/operations.ts @@ -531,6 +531,17 @@ export class ApplyOperations extends TxOperations { if (typeof window === 'object' && window !== null && this.measureName != null) { console.log(`measure ${this.measureName}`, dnow - st, 'server time', result.serverTime) } + if (!result.success) { + console.warn('ops.commit() failed', { + scope: this.scope, + measureName: this.measureName, + matchCount: this.matches.length, + notMatchCount: this.notMatches.length, + txCount: this.txes.length, + matches: this.matches.map((m) => ({ _class: m._class, query: m.query })), + notMatches: this.notMatches.map((m) => ({ _class: m._class, query: m.query })) + }) + } this.txes = [] return { result: result.success, diff --git a/packages/core/src/predicate.ts b/packages/core/src/predicate.ts index 4fbdc47dd1..7bd5d7138d 100644 --- a/packages/core/src/predicate.ts +++ b/packages/core/src/predicate.ts @@ -114,6 +114,9 @@ const predicates: Record = { $size: (o, propertyKey) => { return (docs) => execPredicate(docs, propertyKey, (value) => { + if (value == null) { + return false + } if (!Array.isArray(value)) { throw new Error('$size predicate requires array') } diff --git a/packages/core/src/storage.ts b/packages/core/src/storage.ts index a97a84b076..06e1fb0f99 100644 --- a/packages/core/src/storage.ts +++ b/packages/core/src/storage.ts @@ -18,6 +18,7 @@ import type { Asset, Resource } from '@hcengineering/platform' import type { Association, AttachedDoc, Class, Doc, Domain, Ref, Space } from './classes' import type { Tx } from './tx' import type { KeysByType } from './utils' +import { VersionableDoc } from './versioning' export type ArraySizeSelector = | { @@ -128,7 +129,7 @@ export type Projection = { [P in keyof T]?: 0 | 1 } -export type AssociationQuery = [Ref, 1 | -1] +export type AssociationQuery = [Ref, 1 | -1] | [Ref, 1 | -1, AssociationQuery[]] /** * @public @@ -205,7 +206,7 @@ export type LookupData = Partial> */ export type WithLookup = T & { $lookup?: LookupData - $associations?: Record + $associations?: Record[]> $source?: { $score: number // Score for document result [key: string]: any @@ -269,7 +270,9 @@ export interface SearchResultDoc { description?: string emojiIcon?: string score?: number - doc: Pick & Partial> + doc: Pick & + Partial> & + Partial> } /** diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index dddc0c0706..7f0fd164f1 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -211,7 +211,7 @@ export function fillDefaults ( const baseClass = hierarchy.isDerived(_class, core.class.AttachedDoc) ? core.class.AttachedDoc : core.class.Doc const attributes = hierarchy.getAllAttributes(_class, baseClass) for (const attribute of attributes) { - if (attribute[1].defaultValue !== undefined) { + if (attribute[1].defaultValue != null) { if ((object as any)[attribute[0]] === undefined) { // Clone default value as it might be an object (e.g. array) ;(object as any)[attribute[0]] = structuredClone(attribute[1].defaultValue) diff --git a/packages/core/src/versioning.ts b/packages/core/src/versioning.ts new file mode 100644 index 0000000000..34db2fd7bb --- /dev/null +++ b/packages/core/src/versioning.ts @@ -0,0 +1,13 @@ +import { Class, Doc, PersonId, Ref } from './classes' + +export interface VersionableDoc extends Doc { + baseId?: Ref + version?: number + isLatest?: boolean + docCreatedBy?: PersonId + readonly?: boolean +} + +export interface VersionableClass extends Class { + enabled: boolean +} diff --git a/packages/hulylake-client/package.json b/packages/hulylake-client/package.json index 8058484b5a..9f50c0bdbe 100644 --- a/packages/hulylake-client/package.json +++ b/packages/hulylake-client/package.json @@ -26,18 +26,18 @@ "devDependencies": { "@hcengineering/platform-rig": "^0.7.19", "@types/jest": "^29.5.5", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "cross-env": "~7.0.3", - "esbuild": "^0.25.9", + "esbuild": "^0.25.10", "eslint": "^8.54.0", "eslint-config-standard-with-typescript": "^40.0.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-n": "^15.4.0", "eslint-plugin-promise": "^6.1.1", "jest": "^29.7.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "ts-jest": "^29.1.1", "typescript": "^5.9.3", "eslint-plugin-svelte": "^2.35.1" diff --git a/packages/measurements/src/__tests__/performance.test.ts b/packages/measurements/src/__tests__/performance.test.ts index 14f0ff3e35..fdb6065973 100644 --- a/packages/measurements/src/__tests__/performance.test.ts +++ b/packages/measurements/src/__tests__/performance.test.ts @@ -42,7 +42,7 @@ describe('performance', () => { // Overhead should be reasonable (typically < 50% for simple operations) // This is informational rather than a strict assertion - expect(overheadPercentage).toBeLessThan(200) + expect(overheadPercentage).toBeLessThan(400) }) it('should measure overhead with deep nested contexts', async () => { @@ -154,7 +154,7 @@ describe('performance', () => { console.log(` Overhead: ${overhead.toFixed(2)}ms (${overheadPercentage.toFixed(2)}%)`) // NoMetricsContext should have very low overhead - expect(overheadPercentage).toBeLessThan(50) + expect(overheadPercentage).toBeLessThan(100) }) }) @@ -195,7 +195,7 @@ describe('performance', () => { console.log(` Collected operations: ${aggregated.measurements.request?.operations ?? 0}`) expect(aggregated.measurements.request).toBeDefined() - expect(overheadPercentage).toBeLessThan(100) // Should be less than 100% overhead + expect(overheadPercentage).toBeLessThan(200) // Should be less than 100% overhead }) }) }) diff --git a/packages/model/package.json b/packages/model/package.json index c78f4227f5..2642624072 100644 --- a/packages/model/package.json +++ b/packages/model/package.json @@ -27,12 +27,12 @@ "@types/toposort": "^2.0.3", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "eslint-plugin-svelte": "^2.35.1" }, "dependencies": { diff --git a/packages/platform/lang/pt-br.json b/packages/platform/lang/pt-br.json new file mode 100644 index 0000000000..fb2673913a --- /dev/null +++ b/packages/platform/lang/pt-br.json @@ -0,0 +1,31 @@ +{ + "status": { + "LoadingPlugin": "Carregando plugin {plugin}...", + "UnknownError": "Erro desconhecido: {message}", + "InvalidId": "Id inválido: {id}", + "BadRequest": "Pedido inválido", + "Forbidden": "Proibido", + "Conflict": "Conflito", + "ExpiredLink": "Este link de convite expirou", + "Unauthorized": "Não autorizado", + "UnknownMethod": "Método desconhecido: {method}", + "InternalServerError": "Erro interno do servidor", + "MaintenanceWarning": "Manutenção programada dentro de", + "MaintenanceWarningTime": "{time, plural, =1 {menos de um minuto} other {# minutos}}", + "AccountNotFound": "Conta não encontrada", + "AccountNotConfirmed": "Conta não confirmada", + "WorkspaceNotFound": "Espaço de trabalho não encontrado", + "InvalidPassword": "Senha inválida", + "AccountAlreadyExists": "Conta já existe", + "WorkspaceRateLimit": "O servidor está ocupado. Por favor, espere um pouco e tente novamente", + "AccountAlreadyConfirmed": "Conta já confirmada", + "WorkspaceAlreadyExists": "Espaço de trabalho já existe", + "InvalidOtp": "Código inválido", + "PasswordLoginLocked": "O login com senha está bloqueado devido a muitas tentativas incorretas. Use um método de login OTP para desbloquear sua conta.", + "InviteNotFound": "Convite com e-mail:{email} não encontrado.", + "WorkspaceLimitReached": "Você atingiu o limite de espaços de trabalho. Entre em contato conosco...", + "ReadOnlyAccount": "Demonstração anônima de convidado", + "SystemAccount": "Conta do sistema", + "SocialIdAlreadyExists": "ID social já existe" + } +} diff --git a/packages/platform/package.json b/packages/platform/package.json index 37f63a720f..3069fe4fd0 100644 --- a/packages/platform/package.json +++ b/packages/platform/package.json @@ -35,7 +35,7 @@ "eslint-plugin-n": "^15.4.0", "eslint-plugin-promise": "^6.1.1", "jest": "^29.7.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "ts-jest": "^29.1.1", "typescript": "^5.9.3", "eslint-plugin-svelte": "^2.35.1" diff --git a/packages/postgres-base/package.json b/packages/postgres-base/package.json index e593bea5a8..e87ae0daf0 100644 --- a/packages/postgres-base/package.json +++ b/packages/postgres-base/package.json @@ -32,7 +32,7 @@ "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "eslint": "^8.54.0", diff --git a/packages/query/package.json b/packages/query/package.json index 5582d9696e..8a062d3997 100644 --- a/packages/query/package.json +++ b/packages/query/package.json @@ -32,7 +32,7 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts index 1ebe6bb662..3babe737be 100644 --- a/packages/query/src/index.ts +++ b/packages/query/src/index.ts @@ -16,6 +16,7 @@ import { Analytics } from '@hcengineering/analytics' import core, { Association, + AssociationQuery, BulkUpdateEvent, Class, Client, @@ -780,7 +781,9 @@ export class LiveQuery implements WithTx, Client { } const docs = q.result.getDocs() for (const doc of docs) { - const docToUpdate = doc.$associations?.[association._id]?.find((it) => it._id === tx.objectId) + const docToUpdate = + doc.$associations?.[`${association._id}_a`]?.find((it) => it._id === tx.objectId) ?? + doc.$associations?.[`${association._id}_b`]?.find((it) => it._id === tx.objectId) if (docToUpdate !== undefined) { if (tx._class === core.class.TxMixin) { TxProcessor.updateMixin4Doc(docToUpdate, tx as TxMixin) @@ -1114,31 +1117,90 @@ export class LiveQuery implements WithTx, Client { if (q.options?.associations === undefined) return if (doc._class !== core.class.Relation) return const relation = doc as Relation - const assoc = q.options.associations.find((p) => p[0] === relation.association) + const assoc = findAssociation(q.options.associations, relation.association) if (assoc !== undefined) { if (q.result instanceof Promise) { q.result = await q.result } - const direct = assoc[1] === 1 - const res = q.result.findDoc(direct ? relation.docA : relation.docB) - if (res === undefined) return - const association = this.getModel().findObject(assoc[0]) - if (association === undefined) return - const docToPush = await this.findOne(direct ? association.classB : association.classA, { - _id: direct ? relation.docB : relation.docA - }) - if (docToPush === undefined) return - const arr = res?.$associations?.[relation.association] ?? [] - arr.push(docToPush) - if (res?.$associations === undefined) { - res.$associations = {} + const res = await this.fillRelationDoc(q.result, q.result.getDocs(), q.options.associations, relation) + if (res) { + this.queriesToUpdate.set(q.id, q) } - res.$associations[relation.association] = arr - q.result.updateDoc(res, false) - this.queriesToUpdate.set(q.id, q) } } + async fillRelationDoc ( + qRes: ResultArray, + docs: WithLookup[], + associations: AssociationQuery[] | undefined, + relation: Relation + ): Promise { + if (associations === undefined) return false + let result = false + for (const assoc of associations) { + if (assoc[0] === relation.association) { + const association = this.getModel().findObject(assoc[0]) + if (association === undefined) continue + const sourceId = assoc[1] === 1 ? relation.docA : relation.docB + const targetId = assoc[1] === 1 ? relation.docB : relation.docA + const direct = assoc[1] === 1 + for (const doc of docs) { + if (doc._id === sourceId) { + const nestedAssoc = assoc[2] + const options: FindOptions | undefined = + nestedAssoc !== undefined + ? { + associations: nestedAssoc + } + : undefined + const docToPush = await this.findOne( + direct ? association.classB : association.classA, + { + _id: targetId + }, + options + ) + if (docToPush === undefined) continue + if (doc.$associations === undefined) { + doc.$associations = {} + } + const key = direct ? 'b' : 'a' + const arr = doc.$associations?.[`${assoc[0]}_${key}`] ?? [] + const exists = arr.findIndex((p) => p._id === docToPush._id) + if (exists !== -1) { + arr[exists] = docToPush + } else { + arr.push(docToPush) + } + doc.$associations[`${assoc[0]}_${key}`] = arr + if (qRes.findDoc(doc._id) !== undefined) { + qRes.updateDoc(doc, false) + } + result = true + } + } + } + } + for (const assoc of associations) { + if (assoc[2] !== undefined) { + const key = assoc[1] === 1 ? 'b' : 'a' + for (const doc of docs) { + const arr = doc.$associations?.[`${assoc[0]}_${key}`] ?? [] + if (arr.length > 0) { + const res = await this.fillRelationDoc(qRes, arr, assoc[2], relation) + if (res) { + result = true + if (qRes.findDoc(doc._id) !== undefined) { + qRes.updateDoc(doc, false) + } + } + } + } + } + } + return result + } + private async handleDocAddLookup (q: Query, doc: Doc): Promise { if (q.options?.lookup === undefined) return const lookup = q.options.lookup @@ -1608,3 +1670,20 @@ function getNestedLookup (lookup: Ref> | [Ref>, Lookup> | [Ref>, Lookup]): Ref> { return Array.isArray(lookup) ? lookup[0] : lookup } + +function findAssociation ( + associations: AssociationQuery[], + association: Ref +): AssociationQuery | undefined { + for (const assoc of associations) { + if (assoc[0] === association) return assoc + } + for (const assoc of associations) { + if (assoc[2] !== undefined) { + const res = findAssociation(assoc[2], association) + if (res !== undefined) { + return assoc + } + } + } +} diff --git a/packages/rank/package.json b/packages/rank/package.json index 554002666b..4ea2240f95 100644 --- a/packages/rank/package.json +++ b/packages/rank/package.json @@ -32,7 +32,7 @@ "eslint-plugin-n": "^15.4.0", "eslint-plugin-promise": "^6.1.1", "eslint": "^8.54.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", diff --git a/packages/retry/package.json b/packages/retry/package.json index 5e0514926e..a36f87a810 100644 --- a/packages/retry/package.json +++ b/packages/retry/package.json @@ -26,9 +26,9 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", diff --git a/packages/rpc/package.json b/packages/rpc/package.json index ea3bec09e4..995312c3ff 100644 --- a/packages/rpc/package.json +++ b/packages/rpc/package.json @@ -25,7 +25,7 @@ }, "devDependencies": { "@hcengineering/platform-rig": "^0.7.19", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "@typescript-eslint/eslint-plugin": "^6.21.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-promise": "^6.1.1", @@ -33,7 +33,7 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/storage-client/package.json b/packages/storage-client/package.json index 46612a97c0..818b74d1b9 100644 --- a/packages/storage-client/package.json +++ b/packages/storage-client/package.json @@ -26,16 +26,16 @@ "devDependencies": { "cross-env": "~7.0.3", "@hcengineering/platform-rig": "^0.7.19", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "@typescript-eslint/eslint-plugin": "^6.21.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-n": "^15.4.0", "eslint": "^8.54.0", - "esbuild": "^0.25.9", + "esbuild": "^0.25.10", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/storage-client/src/client/datalake.ts b/packages/storage-client/src/client/datalake.ts index 24c9476d13..ad1dc1f7aa 100644 --- a/packages/storage-client/src/client/datalake.ts +++ b/packages/storage-client/src/client/datalake.ts @@ -17,6 +17,11 @@ import { concatLink } from '@hcengineering/core' import { FileStorage, FileStorageUploadOptions } from '../types' import { uploadMultipart, uploadXhr } from '../upload' +const getPathname = (url: string): string => { + const base = window?.location?.href !== undefined ? window.location.href : 'http://localhost' + return new URL(url, base).pathname +} + /** @public */ export class DatalakeStorage implements FileStorage { constructor (private readonly baseUrl: string) {} @@ -26,6 +31,11 @@ export class DatalakeStorage implements FileStorage { return concatLink(this.baseUrl, path) } + getCookiePath (workspace: string): string { + const url = concatLink(this.baseUrl, `/blob/${workspace}`) + return getPathname(url) + } + async getFileMeta (token: string, workspace: string, file: string): Promise> { const url = concatLink(this.baseUrl, `/meta/${encodeURIComponent(workspace)}/${encodeURIComponent(file)}`) try { diff --git a/packages/storage-client/src/client/front.ts b/packages/storage-client/src/client/front.ts index 67ade0229c..54fa4cd0b9 100644 --- a/packages/storage-client/src/client/front.ts +++ b/packages/storage-client/src/client/front.ts @@ -17,6 +17,11 @@ import { concatLink } from '@hcengineering/core' import { FileStorage, FileStorageUploadOptions } from '../types' import { uploadXhr } from '../upload' +const getPathname = (url: string): string => { + const base = window?.location?.href !== undefined ? window.location.href : 'http://localhost' + return new URL(url, base).pathname +} + /** @public */ export class FrontStorage implements FileStorage { constructor (private readonly baseUrl: string) {} @@ -26,6 +31,11 @@ export class FrontStorage implements FileStorage { return concatLink(this.baseUrl, path) } + getCookiePath (workspace: string): string { + const url = concatLink(this.baseUrl, `/${workspace}`) + return getPathname(url) + } + async getFileMeta (token: string, workspace: string, file: string): Promise> { return {} } diff --git a/packages/storage-client/src/client/hulylake.ts b/packages/storage-client/src/client/hulylake.ts index 397815c015..00acdb31f6 100644 --- a/packages/storage-client/src/client/hulylake.ts +++ b/packages/storage-client/src/client/hulylake.ts @@ -17,6 +17,11 @@ import { concatLink } from '@hcengineering/core' import { FileStorage, FileStorageUploadOptions } from '../types' import { uploadXhr } from '../upload' +const getPathname = (url: string): string => { + const base = window?.location?.href !== undefined ? window.location.href : 'http://localhost' + return new URL(url, base).pathname +} + /** @public */ export class HulylakeStorage implements FileStorage { constructor (private readonly baseUrl: string) {} @@ -26,6 +31,11 @@ export class HulylakeStorage implements FileStorage { return concatLink(this.baseUrl, path) } + getCookiePath (workspace: string): string { + const url = concatLink(this.baseUrl, `/api/${workspace}`) + return getPathname(url) + } + async getFileMeta (token: string, workspace: string, file: string): Promise> { return {} } diff --git a/packages/storage-client/src/types.ts b/packages/storage-client/src/types.ts index bcbe88be8f..45924bc3dd 100644 --- a/packages/storage-client/src/types.ts +++ b/packages/storage-client/src/types.ts @@ -29,6 +29,7 @@ export interface FileStorageUploadOptions { /** @public */ export interface FileStorage { getFileUrl: (workspace: string, file: string, filename?: string) => string + getCookiePath: (workspace: string) => string getFileMeta: (token: string, workspace: string, file: string) => Promise> uploadFile: ( token: string, diff --git a/packages/storage/package.json b/packages/storage/package.json index f4e8f374f8..332a78edc1 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -26,12 +26,12 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "eslint-plugin-svelte": "^2.35.1" }, "dependencies": { diff --git a/packages/text-core/package.json b/packages/text-core/package.json index e174424822..db983467df 100644 --- a/packages/text-core/package.json +++ b/packages/text-core/package.json @@ -32,13 +32,13 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", "@types/markdown-it": "~13.0.0", - "jest-environment-jsdom": "^29.7.0", + "jest-environment-jsdom": "^30.2.0", "eslint-plugin-svelte": "^2.35.1" }, "dependencies": { diff --git a/packages/text-html/package.json b/packages/text-html/package.json index 3a976b5c54..285150becb 100644 --- a/packages/text-html/package.json +++ b/packages/text-html/package.json @@ -32,7 +32,7 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/text-markdown/package.json b/packages/text-markdown/package.json index c1038a9015..b168f402b9 100644 --- a/packages/text-markdown/package.json +++ b/packages/text-markdown/package.json @@ -32,7 +32,7 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/text-ydoc/package.json b/packages/text-ydoc/package.json index 8adc4738e5..17bfc1cbae 100644 --- a/packages/text-ydoc/package.json +++ b/packages/text-ydoc/package.json @@ -32,12 +32,12 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", - "jest-environment-jsdom": "^29.7.0", + "jest-environment-jsdom": "^30.2.0", "fast-equals": "^5.2.2", "y-prosemirror": "^1.3.7", "eslint-plugin-svelte": "^2.35.1" diff --git a/packages/text/package.json b/packages/text/package.json index bde8a84036..3cb1d6d457 100644 --- a/packages/text/package.json +++ b/packages/text/package.json @@ -32,7 +32,7 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1", diff --git a/packages/token/package.json b/packages/token/package.json index 2efedf2f7b..43d7938135 100644 --- a/packages/token/package.json +++ b/packages/token/package.json @@ -25,7 +25,7 @@ }, "devDependencies": { "@hcengineering/platform-rig": "^0.7.19", - "@types/node": "^22.15.29", + "@types/node": "^22.18.1", "@typescript-eslint/eslint-plugin": "^6.21.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-promise": "^6.1.1", @@ -33,7 +33,7 @@ "eslint": "^8.54.0", "@typescript-eslint/parser": "^6.21.0", "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", + "prettier": "^3.6.2", "typescript": "^5.9.3", "jest": "^29.7.0", "ts-jest": "^29.1.1",