diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index d26d0736195..983c40ffc4c 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -164,15 +164,6 @@ dependencies: '@rush-temp/cloud-transactor': specifier: file:./projects/cloud-transactor.tgz version: file:projects/cloud-transactor.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(ts-node@10.9.2)(utf-8-validate@6.0.4) - '@rush-temp/cloud-transactor-api': - specifier: file:./projects/cloud-transactor-api.tgz - version: file:projects/cloud-transactor-api.tgz(esbuild@0.24.2)(ts-node@10.9.2) - '@rush-temp/cloud-transactor-api-demo': - specifier: file:./projects/cloud-transactor-api-demo.tgz - version: file:projects/cloud-transactor-api-demo.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2)(utf-8-validate@6.0.4) - '@rush-temp/cloud-transactor-http-api': - specifier: file:./projects/cloud-transactor-http-api.tgz - version: file:projects/cloud-transactor-http-api.tgz(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2)(utf-8-validate@6.0.4) '@rush-temp/collaboration': specifier: file:./projects/collaboration.tgz version: file:projects/collaboration.tgz(esbuild@0.24.2)(ts-node@10.9.2) @@ -23038,106 +23029,6 @@ packages: - utf-8-validate dev: false - file:projects/cloud-transactor-api-demo.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-jDFW5bSJFL4PuJt0ESJ50jyMHtQKSd9eLfjoUXslmIUdAEITbEEz8N0cstMBujEybfmSghSi6haE2GnG/p4rHg==, tarball: file:projects/cloud-transactor-api-demo.tgz} - id: file:projects/cloud-transactor-api-demo.tgz - name: '@rush-temp/cloud-transactor-api-demo' - version: 0.0.0 - dependencies: - '@cloudflare/workers-types': 4.20241022.0 - '@types/jest': 29.5.12 - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2) - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2) - eslint: 8.56.0 - eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2) - eslint-plugin-import: 2.29.1(eslint@8.56.0) - eslint-plugin-n: 15.7.0(eslint@8.56.0) - eslint-plugin-promise: 6.1.1(eslint@8.56.0) - itty-router: 5.0.18 - jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) - prettier: 3.2.5 - ts-jest: 29.1.2(esbuild@0.24.2)(jest@29.7.0)(typescript@5.6.2) - typescript: 5.6.2 - wrangler: 3.97.0(@cloudflare/workers-types@4.20241022.0)(bufferutil@4.0.8)(utf-8-validate@6.0.4) - transitivePeerDependencies: - - '@babel/core' - - '@jest/types' - - '@types/node' - - babel-jest - - babel-plugin-macros - - bufferutil - - esbuild - - node-notifier - - supports-color - - ts-node - - utf-8-validate - dev: false - - file:projects/cloud-transactor-api.tgz(esbuild@0.24.2)(ts-node@10.9.2): - resolution: {integrity: sha512-KdCN6yGHiBNfGsSWtSwQuIfOQGGr0P40cj5u8Z5p5AQz0MbeoRJ1gFnpzI8VJjOhKfNKn3JR8Zdwg+JTq29yWQ==, tarball: file:projects/cloud-transactor-api.tgz} - id: file:projects/cloud-transactor-api.tgz - name: '@rush-temp/cloud-transactor-api' - version: 0.0.0 - dependencies: - '@types/jest': 29.5.12 - '@types/node': 20.11.19 - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2) - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2) - eslint: 8.56.0 - eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2) - eslint-plugin-import: 2.29.1(eslint@8.56.0) - eslint-plugin-n: 15.7.0(eslint@8.56.0) - eslint-plugin-promise: 6.1.1(eslint@8.56.0) - jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) - prettier: 3.2.5 - ts-jest: 29.1.2(esbuild@0.24.2)(jest@29.7.0)(typescript@5.6.2) - typescript: 5.6.2 - transitivePeerDependencies: - - '@babel/core' - - '@jest/types' - - babel-jest - - babel-plugin-macros - - esbuild - - node-notifier - - supports-color - - ts-node - dev: false - - file:projects/cloud-transactor-http-api.tgz(bufferutil@4.0.8)(esbuild@0.24.2)(ts-node@10.9.2)(utf-8-validate@6.0.4): - resolution: {integrity: sha512-4vV2jdYPYycGKl0jomdYmnEvZgancPc2pdhJ6khPP3qR4FZFhHx5lF/uLuREoZ3d7GuQqNs4J+fKL/+iriCkaA==, tarball: file:projects/cloud-transactor-http-api.tgz} - id: file:projects/cloud-transactor-http-api.tgz - name: '@rush-temp/cloud-transactor-http-api' - version: 0.0.0 - dependencies: - '@cloudflare/workers-types': 4.20241022.0 - '@types/jest': 29.5.12 - '@types/node': 20.11.19 - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.6.2) - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.2) - eslint: 8.56.0 - eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.6.2) - eslint-plugin-import: 2.29.1(eslint@8.56.0) - eslint-plugin-n: 15.7.0(eslint@8.56.0) - eslint-plugin-promise: 6.1.1(eslint@8.56.0) - itty-router: 5.0.18 - jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2) - prettier: 3.2.5 - ts-jest: 29.1.2(esbuild@0.24.2)(jest@29.7.0)(typescript@5.6.2) - typescript: 5.6.2 - wrangler: 3.97.0(@cloudflare/workers-types@4.20241022.0)(bufferutil@4.0.8)(utf-8-validate@6.0.4) - transitivePeerDependencies: - - '@babel/core' - - '@jest/types' - - babel-jest - - babel-plugin-macros - - bufferutil - - esbuild - - node-notifier - - supports-color - - ts-node - - utf-8-validate - dev: false - file:projects/cloud-transactor.tgz(@types/node@20.11.19)(bufferutil@4.0.8)(ts-node@10.9.2)(utf-8-validate@6.0.4): resolution: {integrity: sha512-6kBv/3KfTsOvsDrfVXQvQwtLM72ltx4oPHc3cJ9xtG82XLeQLLpeJmCy6CNDVOtXaqnagwF5TKm/zCw4sc/dFQ==, tarball: file:projects/cloud-transactor.tgz} id: file:projects/cloud-transactor.tgz diff --git a/packages/presentation/src/utils.ts b/packages/presentation/src/utils.ts index 8abd129f4c9..787882bb067 100644 --- a/packages/presentation/src/utils.ts +++ b/packages/presentation/src/utils.ts @@ -21,6 +21,7 @@ import core, { TxProcessor, getCurrentAccount, reduceCalls, + type Account, type AnyAttribute, type ArrOf, type AttachedDoc, @@ -253,6 +254,18 @@ export function getClient (): TxOperations & Client { return clientProxy } +export type OnClientListener = (client: Client, account: Account) => void +const onClientListeners: OnClientListener[] = [] + +export function onClient (l: OnClientListener): void { + onClientListeners.push(l) + if (client !== undefined) { + setTimeout(() => { + l(client, getCurrentAccount()) + }) + } +} + let txQueue: Tx[] = [] export type RefreshListener = () => void @@ -306,6 +319,10 @@ export async function setClient (_client: Client): Promise { if (needRefresh || globalQueries.length > 0) { await refreshClient(true) } + const acc = getCurrentAccount() + onClientListeners.forEach((l) => { + l(_client, acc) + }) } /** * @public diff --git a/plugins/activity-resources/src/activity.ts b/plugins/activity-resources/src/activity.ts index 89125e90b70..47cab72ebe3 100644 --- a/plugins/activity-resources/src/activity.ts +++ b/plugins/activity-resources/src/activity.ts @@ -14,31 +14,21 @@ // import activity, { type ActivityMessage, type SavedMessage } from '@hcengineering/activity' import core, { type Ref, SortingOrder, type WithLookup } from '@hcengineering/core' +import { createQuery, onClient } from '@hcengineering/presentation' import { writable } from 'svelte/store' -import { createQuery, getClient } from '@hcengineering/presentation' export const savedMessagesStore = writable>>([]) export const messageInFocus = writable | undefined>(undefined) const savedMessagesQuery = createQuery(true) -export function loadSavedMessages (): void { - const client = getClient() - - if (client !== undefined) { - savedMessagesQuery.query( - activity.class.SavedMessage, - { space: core.space.Workspace }, - (res) => { - savedMessagesStore.set(res.filter(({ $lookup }) => $lookup?.attachedTo !== undefined)) - }, - { lookup: { attachedTo: activity.class.ActivityMessage }, sort: { modifiedOn: SortingOrder.Descending } } - ) - } else { - setTimeout(() => { - loadSavedMessages() - }, 50) - } -} - -loadSavedMessages() +onClient(() => { + savedMessagesQuery.query( + activity.class.SavedMessage, + { space: core.space.Workspace }, + (res) => { + savedMessagesStore.set(res.filter(({ $lookup }) => $lookup?.attachedTo !== undefined)) + }, + { lookup: { attachedTo: activity.class.ActivityMessage }, sort: { modifiedOn: SortingOrder.Descending } } + ) +}) diff --git a/plugins/calendar-resources/src/utils.ts b/plugins/calendar-resources/src/utils.ts index a4dfa48fe4a..236c7e0a5aa 100644 --- a/plugins/calendar-resources/src/utils.ts +++ b/plugins/calendar-resources/src/utils.ts @@ -5,12 +5,12 @@ import { type ReccuringInstance, generateEventId } from '@hcengineering/calendar' -import { type IdMap, type Timestamp, getCurrentAccount, toIdMap, type DocumentUpdate } from '@hcengineering/core' -import { createQuery, getClient } from '@hcengineering/presentation' -import { showPopup, closePopup, DAY } from '@hcengineering/ui' +import { type DocumentUpdate, type IdMap, type Timestamp, getCurrentAccount, toIdMap } from '@hcengineering/core' +import { createQuery, getClient, onClient } from '@hcengineering/presentation' +import { closePopup, DAY, showPopup } from '@hcengineering/ui' import { writable } from 'svelte/store' -import calendar from './plugin' import UpdateRecInstancePopup from './components/UpdateRecInstancePopup.svelte' +import calendar from './plugin' export function saveUTC (date: Timestamp): Timestamp { const utcdate = new Date(date) @@ -74,24 +74,14 @@ export const calendarByIdStore = writable>(new Map()) export const calendarStore = writable([]) export const visibleCalendarStore = writable([]) -function fillStores (): void { - const client = getClient() - - if (client !== undefined) { - const query = createQuery(true) - query.query(calendar.class.Calendar, {}, (res) => { - calendarStore.set(res) - visibleCalendarStore.set(res.filter((p) => !p.hidden)) - calendarByIdStore.set(toIdMap(res)) - }) - } else { - setTimeout(() => { - fillStores() - }, 50) - } -} - -fillStores() +const query = createQuery(true) +onClient((client, account) => { + query.query(calendar.class.Calendar, {}, (res) => { + calendarStore.set(res) + visibleCalendarStore.set(res.filter((p) => !p.hidden)) + calendarByIdStore.set(toIdMap(res)) + }) +}) export async function updatePast (ops: DocumentUpdate, object: ReccuringInstance): Promise { const client = getClient() diff --git a/plugins/chunter-resources/src/components/chat/utils.ts b/plugins/chunter-resources/src/components/chat/utils.ts index 203b576a066..f843631f72f 100644 --- a/plugins/chunter-resources/src/components/chat/utils.ts +++ b/plugins/chunter-resources/src/components/chat/utils.ts @@ -28,7 +28,7 @@ import core, { } from '@hcengineering/core' import notification, { type DocNotifyContext } from '@hcengineering/notification' import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources' -import { createQuery, getClient, MessageBox } from '@hcengineering/presentation' +import { createQuery, getClient, MessageBox, onClient } from '@hcengineering/presentation' import { type Action, showPopup } from '@hcengineering/ui' import view from '@hcengineering/view' import workbench, { type SpecialNavModel } from '@hcengineering/workbench' @@ -364,12 +364,9 @@ function archiveActivityChannels (contexts: DocNotifyContext[]): void { ) } +const savedAttachmentsQuery = createQuery(true) export function loadSavedAttachments (): void { - const client = getClient() - - if (client !== undefined) { - const savedAttachmentsQuery = createQuery(true) - + onClient(() => { savedAttachmentsQuery.query( attachment.class.SavedAttachments, { space: core.space.Workspace }, @@ -378,11 +375,7 @@ export function loadSavedAttachments (): void { }, { lookup: { attachedTo: attachment.class.Attachment }, sort: { modifiedOn: SortingOrder.Descending } } ) - } else { - setTimeout(() => { - loadSavedAttachments() - }, 50) - } + }) } export async function hideActivityChannels (contexts: DocNotifyContext[]): Promise { diff --git a/plugins/contact-resources/src/utils.ts b/plugins/contact-resources/src/utils.ts index 93700daccce..e7e7a03a747 100644 --- a/plugins/contact-resources/src/utils.ts +++ b/plugins/contact-resources/src/utils.ts @@ -52,7 +52,7 @@ import core, { } from '@hcengineering/core' import notification, { type DocNotifyContext, type InboxNotification } from '@hcengineering/notification' import { type IntlString, getEmbeddedLabel, getResource, translate } from '@hcengineering/platform' -import { createQuery, getClient } from '@hcengineering/presentation' +import { createQuery, getClient, onClient } from '@hcengineering/presentation' import { type TemplateDataProvider } from '@hcengineering/templates' import { getCurrentResolvedLocation, @@ -323,42 +323,32 @@ export const personByIdStore = derived([personAccountPersonByIdStore, employeeBy return new Map([...m1, ...m2]) }) -function fillStores (): void { - const client = getClient() - - if (client !== undefined) { - const accountPersonQuery = createQuery(true) - - const query = createQuery(true) - query.query(contact.mixin.Employee, { active: { $in: [true, false] } }, (res) => { - employeesStore.set(res) - employeeByIdStore.set(toIdMap(res)) - }) +const query = createQuery(true) +const accountQ = createQuery(true) +const accountPersonQuery = createQuery(true) +const providerQuery = createQuery(true) - const accountQ = createQuery(true) - accountQ.query(contact.class.PersonAccount, {}, (res) => { - personAccountByIdStore.set(toIdMap(res)) +onClient(() => { + query.query(contact.mixin.Employee, { active: { $in: [true, false] } }, (res) => { + employeesStore.set(res) + employeeByIdStore.set(toIdMap(res)) + }) - const persons = res.map((it) => it.person) + accountQ.query(contact.class.PersonAccount, {}, (res) => { + personAccountByIdStore.set(toIdMap(res)) - accountPersonQuery.query(contact.class.Person, { _id: { $in: persons } }, (res) => { - const personIn = toIdMap(res) - personAccountPersonByIdStore.set(personIn) - }) - }) + const persons = res.map((it) => it.person) - const providerQuery = createQuery(true) - providerQuery.query(contact.class.ChannelProvider, {}, (res) => { - channelProviders.set(res) + accountPersonQuery.query(contact.class.Person, { _id: { $in: persons } }, (res) => { + const personIn = toIdMap(res) + personAccountPersonByIdStore.set(personIn) }) - } else { - setTimeout(() => { - fillStores() - }, 50) - } -} + }) -fillStores() + providerQuery.query(contact.class.ChannelProvider, {}, (res) => { + channelProviders.set(res) + }) +}) const userStatusesQuery = createQuery(true) diff --git a/plugins/love-resources/src/stores.ts b/plugins/love-resources/src/stores.ts index 1e77d61a97a..af5cf8054f0 100644 --- a/plugins/love-resources/src/stores.ts +++ b/plugins/love-resources/src/stores.ts @@ -11,7 +11,7 @@ import { type Room, type MeetingMinutes } from '@hcengineering/love' -import { createQuery, getClient } from '@hcengineering/presentation' +import { createQuery, onClient } from '@hcengineering/presentation' import { derived, get, writable } from 'svelte/store' import { personIdByAccountId } from '@hcengineering/contact-resources' import aiBot from '@hcengineering/ai-bot' @@ -79,77 +79,69 @@ function filterParticipantInfo (value: ParticipantInfo[]): ParticipantInfo[] { export const storePromise = writable>(new Promise((resolve) => {})) -function fillStores (): void { - const client = getClient() - if (client !== undefined || getCurrentAccount() != null) { - const query = createQuery(true) - const roomPromise = new Promise((resolve) => - query.query(love.class.Room, {}, (res) => { - rooms.set(res) - resolve() - }) - ) - const statusQuery = createQuery(true) - const infoPromise = new Promise((resolve) => - statusQuery.query(love.class.ParticipantInfo, {}, (res) => { - infos.set(filterParticipantInfo(res)) - resolve() - }) - ) - const floorsQuery = createQuery(true) - const floorPromise = new Promise((resolve) => - floorsQuery.query(love.class.Floor, {}, (res) => { - floors.set(res) +const query = createQuery(true) +const statusQuery = createQuery(true) +const floorsQuery = createQuery(true) +const requestsQuery = createQuery(true) +const preferencesQuery = createQuery(true) +const invitesQuery = createQuery(true) + +onClient(() => { + const roomPromise = new Promise((resolve) => + query.query(love.class.Room, {}, (res) => { + rooms.set(res) + resolve() + }) + ) + const infoPromise = new Promise((resolve) => + statusQuery.query(love.class.ParticipantInfo, {}, (res) => { + infos.set(filterParticipantInfo(res)) + resolve() + }) + ) + const floorPromise = new Promise((resolve) => + floorsQuery.query(love.class.Floor, {}, (res) => { + floors.set(res) + resolve() + }) + ) + const requestPromise = new Promise((resolve) => + requestsQuery.query( + love.class.JoinRequest, + { person: (getCurrentAccount() as PersonAccount).person, status: RequestStatus.Pending }, + (res) => { + myRequests.set(res) resolve() - }) - ) - const requestsQuery = createQuery(true) - const requestPromise = new Promise((resolve) => - requestsQuery.query( - love.class.JoinRequest, - { person: (getCurrentAccount() as PersonAccount).person, status: RequestStatus.Pending }, - (res) => { - myRequests.set(res) - resolve() - } - ) + } ) - const preferencesQuery = createQuery(true) - const preferencePromise = new Promise((resolve) => - preferencesQuery.query(love.class.DevicesPreference, {}, (res) => { - myPreferences.set(res[0]) - $myPreferences = res[0] - resolve() - }) - ) - const invitesQuery = createQuery(true) - const invitesPromise = new Promise((resolve) => - invitesQuery.query(love.class.Invite, { status: RequestStatus.Pending }, (res) => { - invites.set(res) + ) + const preferencePromise = new Promise((resolve) => + preferencesQuery.query(love.class.DevicesPreference, {}, (res) => { + myPreferences.set(res[0]) + $myPreferences = res[0] + resolve() + }) + ) + + const invitesPromise = new Promise((resolve) => + invitesQuery.query(love.class.Invite, { status: RequestStatus.Pending }, (res) => { + invites.set(res) + resolve() + }) + ) + storePromise.set( + new Promise((resolve) => { + void Promise.all([ + roomPromise, + infoPromise, + floorPromise, + requestPromise, + preferencePromise, + invitesPromise + ]).then(() => { resolve() }) - ) - storePromise.set( - new Promise((resolve) => { - void Promise.all([ - roomPromise, - infoPromise, - floorPromise, - requestPromise, - preferencePromise, - invitesPromise - ]).then(() => { - resolve() - }) - }) - ) - } else { - setTimeout(() => { - fillStores() - }, 50) - } -} - -fillStores() - + }) + ) +}) export const lockedRoom = writable('') diff --git a/plugins/task-resources/src/index.ts b/plugins/task-resources/src/index.ts index 537d2c9b46f..bf7456c4d6e 100644 --- a/plugins/task-resources/src/index.ts +++ b/plugins/task-resources/src/index.ts @@ -14,8 +14,10 @@ // limitations under the License. // +import activity from '@hcengineering/activity' +import chunter from '@hcengineering/chunter' +import { type Employee, type PersonAccount } from '@hcengineering/contact' import core, { - getCurrentAccount, toIdMap, type Attribute, type Class, @@ -27,7 +29,7 @@ import core, { type TxOperations } from '@hcengineering/core' import { type IntlString, type Resources } from '@hcengineering/platform' -import { createQuery, getClient } from '@hcengineering/presentation' +import { createQuery, getClient, onClient } from '@hcengineering/presentation' import task, { getStatusIndex, makeRank, @@ -41,9 +43,6 @@ import { getCurrentLocation, navigate, showPopup } from '@hcengineering/ui' import { type ViewletDescriptor } from '@hcengineering/view' import { CategoryQuery, groupBy, statusStore } from '@hcengineering/view-resources' import { get, writable } from 'svelte/store' -import { type Employee, type PersonAccount } from '@hcengineering/contact' -import activity from '@hcengineering/activity' -import chunter from '@hcengineering/chunter' import AssignedTasks from './components/AssignedTasks.svelte' import Dashboard from './components/Dashboard.svelte' @@ -69,14 +68,14 @@ import TaskKindSelector from './components/taskTypes/TaskKindSelector.svelte' import TaskTypeClassPresenter from './components/taskTypes/TaskTypeClassPresenter.svelte' import TaskTypePresenter from './components/taskTypes/TaskTypePresenter.svelte' -import ProjectTypeSelector from './components/projectTypes/ProjectTypeSelector.svelte' +import { employeeByIdStore, personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources' import CreateProjectType from './components/projectTypes/CreateProjectType.svelte' -import ProjectTypeGeneralSectionEditor from './components/projectTypes/ProjectTypeGeneralSectionEditor.svelte' -import ProjectTypeTasksTypeSectionEditor from './components/projectTypes/ProjectTypeTasksTypeSectionEditor.svelte' import ProjectTypeAutomationsSectionEditor from './components/projectTypes/ProjectTypeAutomationsSectionEditor.svelte' import ProjectTypeCollectionsSectionEditor from './components/projectTypes/ProjectTypeCollectionsSectionEditor.svelte' +import ProjectTypeGeneralSectionEditor from './components/projectTypes/ProjectTypeGeneralSectionEditor.svelte' +import ProjectTypeSelector from './components/projectTypes/ProjectTypeSelector.svelte' +import ProjectTypeTasksTypeSectionEditor from './components/projectTypes/ProjectTypeTasksTypeSectionEditor.svelte' import TaskTypeEditor from './components/taskTypes/TaskTypeEditor.svelte' -import { employeeByIdStore, personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources' export { default as AssigneePresenter } from './components/AssigneePresenter.svelte' export { default as TypeSelector } from './components/TypeSelector.svelte' @@ -364,33 +363,24 @@ export const taskTypeStore = writable>(new Map()) export const typesOfJoinedProjectsStore = writable>>() export const joinedProjectsStore = writable() -function fillStores (): void { - const client = getClient() - - if (client !== undefined && getCurrentAccount() != null) { - const query = createQuery(true) - query.query(task.class.ProjectType, {}, (res) => { - typeStore.set(toIdMap(res)) - }) +const query = createQuery(true) +const taskQuery = createQuery(true) +const projectQuery = createQuery(true) - const taskQuery = createQuery(true) - taskQuery.query(task.class.TaskType, {}, (res) => { - taskTypeStore.set(toIdMap(res)) - }) +onClient((client, user) => { + query.query(task.class.ProjectType, {}, (res) => { + typeStore.set(toIdMap(res)) + }) - const projectQuery = createQuery(true) - projectQuery.query(task.class.Project, { members: getCurrentAccount()._id }, (res) => { - typesOfJoinedProjectsStore.set(res.map((r) => r.type).filter((it, idx, arr) => arr.indexOf(it) === idx)) - joinedProjectsStore.set(res) - }) - } else { - setTimeout(() => { - fillStores() - }, 50) - } -} + taskQuery.query(task.class.TaskType, {}, (res) => { + taskTypeStore.set(toIdMap(res)) + }) -fillStores() + projectQuery.query(task.class.Project, { members: user._id }, (res) => { + typesOfJoinedProjectsStore.set(res.map((r) => r.type).filter((it, idx, arr) => arr.indexOf(it) === idx)) + joinedProjectsStore.set(res) + }) +}) export const selectedTypeStore = writable | undefined>(undefined) export const selectedTaskTypeStore = writable | undefined>(undefined) diff --git a/plugins/tracker-resources/src/utils.ts b/plugins/tracker-resources/src/utils.ts index 1d3e380332c..69bdc5ca37e 100644 --- a/plugins/tracker-resources/src/utils.ts +++ b/plugins/tracker-resources/src/utils.ts @@ -36,7 +36,7 @@ import core, { type TxUpdateDoc } from '@hcengineering/core' import { type IntlString } from '@hcengineering/platform' -import { createQuery, getClient } from '@hcengineering/presentation' +import { createQuery, getClient, onClient } from '@hcengineering/presentation' import task, { getStatusIndex, makeRank, type ProjectType } from '@hcengineering/task' import { activeProjects as taskActiveProjects, taskTypeStore } from '@hcengineering/task-resources' import { @@ -577,36 +577,25 @@ export interface IssueRef { export type IssueReverseRevMap = Map, IssueRef[]> export const relatedIssues = writable(new Map()) -function fillStores (): void { - const client = getClient() - - if (client !== undefined) { - const relatedIssuesQuery = createQuery(true) - - relatedIssuesQuery.query( - tracker.class.Issue, - { 'relations._id': { $exists: true } }, - (res) => { - const nMap: IssueReverseRevMap = new Map() - for (const r of res) { - for (const rr of r.relations ?? []) { - nMap.set(rr._id, [...(nMap.get(rr._id) ?? []), { _id: r._id, status: r.status }]) - } - } - relatedIssues.set(nMap) - }, - { - projection: { - relations: 1, - status: 1 +const relatedIssuesQuery = createQuery(true) +onClient(() => { + relatedIssuesQuery.query( + tracker.class.Issue, + { 'relations._id': { $exists: true } }, + (res) => { + const nMap: IssueReverseRevMap = new Map() + for (const r of res) { + for (const rr of r.relations ?? []) { + nMap.set(rr._id, [...(nMap.get(rr._id) ?? []), { _id: r._id, status: r.status }]) } } - ) - } else { - setTimeout(() => { - fillStores() - }, 50) - } -} - -fillStores() + relatedIssues.set(nMap) + }, + { + projection: { + relations: 1, + status: 1 + } + } + ) +}) diff --git a/plugins/view-resources/src/status.ts b/plugins/view-resources/src/status.ts index a3ed74dac58..127f09787a0 100644 --- a/plugins/view-resources/src/status.ts +++ b/plugins/view-resources/src/status.ts @@ -14,7 +14,7 @@ // import core, { type IdMap, type Status, toIdMap } from '@hcengineering/core' -import { createQuery, getClient } from '@hcengineering/presentation' +import { createQuery, onClient } from '@hcengineering/presentation' import { writable } from 'svelte/store' interface Store { @@ -28,23 +28,13 @@ export const statusStore = writable({ array: [] }) -function fillStores (): void { - const client = getClient() - - if (client !== undefined) { - const query = createQuery(true) - query.query(core.class.Status, {}, (res) => { - const obj = { - byId: toIdMap(res), - array: res - } - statusStore.set(obj) - }) - } else { - setTimeout(() => { - fillStores() - }, 50) - } -} - -fillStores() +const query = createQuery(true) +onClient(() => { + query.query(core.class.Status, {}, (res) => { + const obj = { + byId: toIdMap(res), + array: res + } + statusStore.set(obj) + }) +}) diff --git a/rush.json b/rush.json index d167db3568b..12f052a2a55 100644 --- a/rush.json +++ b/rush.json @@ -2230,21 +2230,6 @@ "packageName": "@hcengineering/cloud-transactor", "projectFolder": "workers/transactor", "shouldPublish": false - }, - { - "packageName": "@hcengineering/cloud-transactor-api", - "projectFolder": "workers/transactor-api", - "shouldPublish": false - }, - { - "packageName": "@hcengineering/cloud-transactor-api-demo", - "projectFolder": "workers/transactor-api-demo", - "shouldPublish": false - }, - { - "packageName": "@hcengineering/cloud-transactor-http-api", - "projectFolder": "workers/transactor-http-api", - "shouldPublish": false } ] } diff --git a/workers/transactor-api-demo/.eslintrc.js b/workers/transactor-api-demo/.eslintrc.js deleted file mode 100644 index 72235dc2833..00000000000 --- a/workers/transactor-api-demo/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'], - parserOptions: { - tsconfigRootDir: __dirname, - project: './tsconfig.json' - } -} diff --git a/workers/transactor-api-demo/config/rig.json b/workers/transactor-api-demo/config/rig.json deleted file mode 100644 index 0110930f55e..00000000000 --- a/workers/transactor-api-demo/config/rig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@hcengineering/platform-rig" -} diff --git a/workers/transactor-api-demo/jest.config.js b/workers/transactor-api-demo/jest.config.js deleted file mode 100644 index 2cfd408b679..00000000000 --- a/workers/transactor-api-demo/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], - roots: ["./src"], - coverageReporters: ["text-summary", "html"] -} diff --git a/workers/transactor-api-demo/package.json b/workers/transactor-api-demo/package.json deleted file mode 100644 index 507fe3f4538..00000000000 --- a/workers/transactor-api-demo/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "@hcengineering/cloud-transactor-api-demo", - "version": "0.6.0", - "main": "lib/index.js", - "types": "types/index.d.ts", - "template": "cloud", - "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev --port 4050", - "dev-local": "wrangler dev --port 4050 --local --upstream-protocol=http", - "start": "wrangler dev --port 4050", - "cf-typegen": "wrangler types", - "build": "compile", - "build:watch": "compile", - "test": "jest --passWithNoTests --silent --forceExit", - "format": "format src", - "_phase:build": "compile transpile src", - "_phase:test": "jest --passWithNoTests --silent --forceExit", - "_phase:format": "format src", - "_phase:validate": "compile validate" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20241022.0", - "@hcengineering/platform-rig": "^0.6.0", - "@types/jest": "^29.5.5", - "@typescript-eslint/eslint-plugin": "^6.11.0", - "@typescript-eslint/parser": "^6.11.0", - "eslint-config-standard-with-typescript": "^40.0.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-n": "^15.4.0", - "eslint": "^8.54.0", - "jest": "^29.7.0", - "prettier": "^3.1.0", - "ts-jest": "^29.1.1", - "typescript": "^5.3.3", - "wrangler": "^3.97.0" - }, - "dependencies": { - "@hcengineering/core": "^0.6.32", - "@hcengineering/contact": "^0.6.24", - "@hcengineering/cloud-transactor-api": "^0.6.0", - "itty-router": "^5.0.18" - } -} diff --git a/workers/transactor-api-demo/src/index.ts b/workers/transactor-api-demo/src/index.ts deleted file mode 100644 index e4c509a59c2..00000000000 --- a/workers/transactor-api-demo/src/index.ts +++ /dev/null @@ -1,166 +0,0 @@ -// -// Copyright © 2024 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import { - type ConnectOptions, - type TransactorRawApi, - type TransactorService, - createHttpClient, - createRpcClient, - getWorkspaceLogin, - unpackModel -} from '@hcengineering/cloud-transactor-api' -import contact, { AvatarType, type Person } from '@hcengineering/contact' -import core, { type AccountClient, type Ref, type TxCreateDoc, TxOperations, generateId } from '@hcengineering/core' -import { Router, error, json } from 'itty-router' - -async function callClient (client: T, method: () => Promise): Promise { - try { - return json(await method()) - } catch (error) { - console.error({ error }) - throw error - } finally { - if (Symbol.dispose in (client as any)) { - ;(client as any)[Symbol.dispose]() - } - } -} - -export default { - async fetch (request: Request, env: Env, ctx: ExecutionContext): Promise { - const transactorService = env.TRANSACTOR_SERVICE as any as TransactorService - - function getConnectOpts (params: Record): ConnectOptions { - return { - authOptions: { - email: params.email, - password: params.password, - workspace: params.workspace - }, - serverConfig: { - ACCOUNTS_URL: env.ACCOUNTS_URL - }, - workspaceId: params.workspace, - loadModel: params.loadModel === true - } - } - - async function rawClient (params: Record): Promise { - const email = params.email - const password = params.password - const workspace = params.workspace - const info = await getWorkspaceLogin('', { email, password, workspace }, { ACCOUNTS_URL: env.ACCOUNTS_URL }) - return await transactorService.openRpc(info.token, params.workspace) - } - - async function rpcClient (params: Record): Promise { - return await createRpcClient(transactorService, getConnectOpts(params)) - } - - async function httpClient (params: Record): Promise { - return await createHttpClient(env.HTTP_API_URL, getConnectOpts(params)) - } - - const router = Router() - router - .get('/demo-find-raw/:email/:password/:workspace', async ({ params }) => { - const client = await rawClient(params) - return await callClient(client, async () => { - return await client.findAll(contact.class.Person, {}) - }) - }) - .get('/demo-find-rpc/:email/:password/:workspace', async ({ params }) => { - const client = await rpcClient(params) - return await callClient(client, async () => { - return await client.findAll(contact.class.Person, {}) - }) - }) - .get('/demo-find-http/:email/:password/:workspace', async ({ params }) => { - const client = await httpClient(params) - return await callClient(client, async () => { - return await client.findAll(contact.class.Person, {}) - }) - }) - .get('/demo-get-model-raw/:email/:password/:workspace', async ({ params }) => { - const client = await rawClient(params) - return await callClient(client, async () => { - return await unpackModel(await client.getModel()) - }) - }) - .get('/demo-get-model-rpc/:email/:password/:workspace', async ({ params }) => { - const client = await rpcClient({ ...params, loadModel: true }) - return await callClient(client, async () => { - return client.getModel() - }) - }) - .get('/demo-get-model-http/:email/:password/:workspace', async ({ params }) => { - const client = await httpClient({ ...params, loadModel: true }) - return await callClient(client, async () => { - return client.getModel() - }) - }) - .get('/demo-tx-raw/:email/:password/:workspace', async ({ params }) => { - const client = await rawClient(params) - return await callClient(client, async () => { - const account = await client.getAccount() - const id = generateId() - const tx: TxCreateDoc = { - _id: id as Ref>, - _class: core.class.TxCreateDoc, - space: core.space.Tx, - objectId: id as Ref, - objectClass: contact.class.Person, - objectSpace: account.space, - modifiedOn: Date.now(), - modifiedBy: account._id, - createdBy: account._id, - attributes: { - name: 'Person ' + id, - city: 'Unknown', - avatarType: AvatarType.COLOR - } - } - return await client.tx(tx) - }) - }) - .get('/demo-tx-rpc/:email/:password/:workspace', async ({ params }) => { - const client = await rpcClient({ ...params, loadModel: true }) - return await callClient(client, async () => { - const account = await client.getAccount() - const txops = new TxOperations(client, account._id) - return await txops.createDoc(contact.class.Person, account.space, { - name: 'Person ' + generateId(), - city: 'Unknown', - avatarType: AvatarType.COLOR - }) - }) - }) - .get('/demo-tx-http/:email/:password/:workspace', async ({ params }) => { - const client = await httpClient({ ...params, loadModel: true }) - return await callClient(client, async () => { - const account = await client.getAccount() - const txops = new TxOperations(client, account._id) - return await txops.createDoc(contact.class.Person, account.space, { - name: 'Person ' + generateId(), - city: 'Unknown', - avatarType: AvatarType.COLOR - }) - }) - }) - .all('*', () => error(404)) - return await router.fetch(request).catch(error) - } -} satisfies ExportedHandler diff --git a/workers/transactor-api-demo/tsconfig.json b/workers/transactor-api-demo/tsconfig.json deleted file mode 100644 index 6b38a447d20..00000000000 --- a/workers/transactor-api-demo/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json", - - "compilerOptions": { - "rootDir": "./src", - "outDir": "./lib", - "declarationDir": "./types", - "tsBuildInfoFile": ".build/build.tsbuildinfo", - "types": ["@cloudflare/workers-types", "jest"] - } -} diff --git a/workers/transactor-api-demo/worker-configuration.d.ts b/workers/transactor-api-demo/worker-configuration.d.ts deleted file mode 100644 index c6ccb50e45d..00000000000 --- a/workers/transactor-api-demo/worker-configuration.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Generated by Wrangler by running `wrangler types` - -interface Env { - ACCOUNTS_URL: "http://127.0.0.1:3000"; - HTTP_API_URL: "http://127.0.0.1:4040"; - TRANSACTOR_SERVICE: Fetcher; -} diff --git a/workers/transactor-api-demo/wrangler.toml b/workers/transactor-api-demo/wrangler.toml deleted file mode 100644 index f31c815405d..00000000000 --- a/workers/transactor-api-demo/wrangler.toml +++ /dev/null @@ -1,15 +0,0 @@ -#:schema node_modules/wrangler/config-schema.json -name = "cloud-transactor-api-demo" -main = "src/index.ts" -compatibility_date = "2024-09-23" -compatibility_flags = ["nodejs_compat"] -keep_vars = true - -[vars] -ACCOUNTS_URL = "http://127.0.0.1:3000" -HTTP_API_URL = "http://127.0.0.1:4040" - -[[services]] -binding = "TRANSACTOR_SERVICE" -service = "cloud-transactor" -entrypoint = "TransactorRpc" diff --git a/workers/transactor-api/.eslintrc.js b/workers/transactor-api/.eslintrc.js deleted file mode 100644 index ce90fb9646f..00000000000 --- a/workers/transactor-api/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - extends: ['./node_modules/@hcengineering/platform-rig/profiles/node/eslint.config.json'], - parserOptions: { - tsconfigRootDir: __dirname, - project: './tsconfig.json' - } -} diff --git a/workers/transactor-api/config/rig.json b/workers/transactor-api/config/rig.json deleted file mode 100644 index 78cc5a17334..00000000000 --- a/workers/transactor-api/config/rig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@hcengineering/platform-rig", - "rigProfile": "node" -} diff --git a/workers/transactor-api/jest.config.js b/workers/transactor-api/jest.config.js deleted file mode 100644 index 2cfd408b679..00000000000 --- a/workers/transactor-api/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], - roots: ["./src"], - coverageReporters: ["text-summary", "html"] -} diff --git a/workers/transactor-api/package.json b/workers/transactor-api/package.json deleted file mode 100644 index 81f7d9dce04..00000000000 --- a/workers/transactor-api/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@hcengineering/cloud-transactor-api", - "version": "0.6.0", - "main": "lib/index.js", - "types": "types/index.d.ts", - "template": "node", - "scripts": { - "build": "compile", - "build:watch": "compile", - "test": "jest --passWithNoTests --silent --forceExit", - "format": "format src", - "_phase:build": "compile transpile src", - "_phase:test": "jest --passWithNoTests --silent --forceExit", - "_phase:format": "format src", - "_phase:validate": "compile validate" - }, - "devDependencies": { - "@hcengineering/platform-rig": "^0.6.0", - "@types/jest": "^29.5.5", - "@types/node": "~20.11.16", - "@typescript-eslint/eslint-plugin": "^6.11.0", - "@typescript-eslint/parser": "^6.11.0", - "eslint-config-standard-with-typescript": "^40.0.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-n": "^15.4.0", - "eslint": "^8.54.0", - "jest": "^29.7.0", - "prettier": "^3.1.0", - "ts-jest": "^29.1.1", - "typescript": "^5.3.3" - }, - "dependencies": { - "@hcengineering/core": "^0.6.32" - } -} diff --git a/workers/transactor-api/src/account.ts b/workers/transactor-api/src/account.ts deleted file mode 100644 index d393f4c0a8d..00000000000 --- a/workers/transactor-api/src/account.ts +++ /dev/null @@ -1,137 +0,0 @@ -// -// Copyright © 2024 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import { concatLink } from '@hcengineering/core' - -/** - * Configuration options for password-based authentication - * @public - */ -export interface PasswordAuthOptions { - /** User's email address */ - email: string - - /** User's password */ - password: string - - /** Workspace name */ - workspace: string -} - -/** - * Configuration options for token-based authentication - * @public - */ -export interface TokenAuthOptions { - /** Authentication token */ - token: string - - /** Workspace name */ - workspace: string -} - -/** - * Union type representing all authentication options - * Can be either password-based or token-based authentication - * @public - */ -export type AuthOptions = PasswordAuthOptions | TokenAuthOptions - -/** @public */ -export interface LoginInfo { - token: string - endpoint: string - confirmed: boolean - email: string -} - -/** @public */ -export interface WorkspaceLoginInfo extends LoginInfo { - workspace: string - workspaceId: string -} - -export interface ServerConfig { - ACCOUNTS_URL: string -} - -async function loadServerConfig (url: string): Promise { - const configUrl = concatLink(url, '/config.json') - const res = await fetch(configUrl) - if (res.ok) { - return (await res.json()) as ServerConfig - } - throw new Error('Failed to fetch config') -} - -export async function getWorkspaceLogin ( - configUrl: string, - options: AuthOptions, - serverConfig?: ServerConfig -): Promise { - serverConfig ??= await loadServerConfig(configUrl) - - let token: string - - if ('token' in options) { - token = options.token - } else { - const { email, password, workspace } = options - token = await login(serverConfig.ACCOUNTS_URL, email, password, workspace) - } - - if (token === undefined) { - throw new Error('Login failed') - } - - const ws = await selectWorkspace(serverConfig.ACCOUNTS_URL, token, options.workspace) - if (ws === undefined) { - throw new Error('Workspace not found') - } - - return ws -} - -async function login (accountsUrl: string, user: string, password: string, workspace: string): Promise { - const response = await fetch(accountsUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - method: 'login', - params: [user, password, workspace] - }) - }) - - const result = await response.json() - return result.result?.token -} - -async function selectWorkspace (accountsUrl: string, token: string, workspace: string): Promise { - const response = await fetch(accountsUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer ' + token - }, - body: JSON.stringify({ - method: 'selectWorkspace', - params: [workspace, 'external'] - }) - }) - const result = await response.json() - return result.result as WorkspaceLoginInfo -} diff --git a/workers/transactor-api/src/http.ts b/workers/transactor-api/src/http.ts deleted file mode 100644 index 13d44d6bfd9..00000000000 --- a/workers/transactor-api/src/http.ts +++ /dev/null @@ -1,164 +0,0 @@ -// -// Copyright © 2024 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import { - type Account, - type AccountClient, - type Class, - type Doc, - type DocumentQuery, - type FindOptions, - type FindResult, - type ModelDb, - type Tx, - type TxResult, - type Ref, - type Hierarchy, - type SearchQuery, - type SearchOptions, - type SearchResult, - type WithLookup -} from '@hcengineering/core' -import { type ConnectOptions } from './types' -import { getWorkspaceLogin } from './account' -import { decodeModel } from './utils' - -export async function createHttpClient (httpApiWorkerUrl: string, options: ConnectOptions): Promise { - let token = options.workspaceToken - if (token === undefined) { - if (options.authOptions === undefined) { - throw new Error('Either workspaceToken or authOptions must be provided') - } - if (options.configUrl === '' && options.serverConfig === undefined) { - throw new Error('Either configUrl or serverConfig must be provided') - } - const ws = await getWorkspaceLogin(options.configUrl ?? '', options.authOptions, options.serverConfig) - token = ws.token - } - const client = new TransactorHttpClient(token, options.workspaceId ?? '', httpApiWorkerUrl) - if (options.loadModel ?? false) { - await client.loadModel() - } - return client -} - -class TransactorHttpClient implements AccountClient { - private model: ModelDb | undefined - private hierarchy: Hierarchy | undefined - - constructor ( - private readonly token: string, - private readonly workspaceId: string, - private readonly httpApiWorkerUrl: string - ) {} - - private url (method: string): string { - return `${this.httpApiWorkerUrl}/${method}/${encodeURIComponent(this.workspaceId)}` - } - - async loadModel (): Promise { - const response = await fetch(this.url('model'), { - method: 'GET', - headers: { - Accept: 'application/octet-stream', - Authorization: 'Bearer ' + this.token - } - }) - const compressed = await (response as any).bytes() - const { model, hierarchy } = await decodeModel(compressed) - this.model = model - this.hierarchy = hierarchy - } - - notify (...tx: Tx[]): void { - // does nothing - } - - getHierarchy (): Hierarchy { - if (this.hierarchy === undefined) { - throw new Error('Hierarchy is not loaded, please use loadModel=true when initializing client') - } - return this.hierarchy - } - - getModel (): ModelDb { - if (this.model === undefined) { - throw new Error('Model is not loaded, please use loadModel=true when initializing client') - } - return this.model - } - - async findOne( - _class: Ref>, - query: DocumentQuery, - options?: FindOptions - ): Promise | undefined> { - return (await this.findAll(_class, query, options)).shift() - } - - async close (): Promise { - // does nothing - } - - async findAll( - _class: Ref>, - query: DocumentQuery, - options?: FindOptions - ): Promise> { - const response = await fetch(this.url('find-all'), { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: 'Bearer ' + this.token - }, - body: JSON.stringify({ _class, query, options }) - }) - return await response.json() - } - - async tx (tx: Tx): Promise { - const response = await fetch(this.url('tx'), { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: 'Bearer ' + this.token - }, - body: JSON.stringify(tx) - }) - return await response.json() - } - - async searchFulltext (query: SearchQuery, options: SearchOptions): Promise { - // TODO - const result: SearchResult = { - docs: [], - total: 0 - } - return result - } - - async getAccount (): Promise { - const response = await fetch(this.url('account'), { - method: 'GET', - headers: { - Accept: 'application/json', - Authorization: 'Bearer ' + this.token - } - }) - return await response.json() - } -} diff --git a/workers/transactor-api/src/index.ts b/workers/transactor-api/src/index.ts deleted file mode 100644 index 9f28d8930b9..00000000000 --- a/workers/transactor-api/src/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright © 2024 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -export * from './http' -export * from './account' -export * from './rpc' -export * from './types' -export * from './utils' diff --git a/workers/transactor-api/src/rpc.ts b/workers/transactor-api/src/rpc.ts deleted file mode 100644 index dc6739d3ad4..00000000000 --- a/workers/transactor-api/src/rpc.ts +++ /dev/null @@ -1,169 +0,0 @@ -// -// Copyright © 2024 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import { - type Account, - type AccountClient, - type Class, - type Doc, - type DocumentQuery, - type FindOptions, - type FindResult, - type Ref, - type SearchOptions, - type SearchQuery, - type SearchResult, - type Storage, - type Tx, - type TxResult, - type WithLookup, - type Hierarchy, - type ModelDb -} from '@hcengineering/core' -import { decodeModel } from './utils' -import { type ConnectOptions } from './types' -import { getWorkspaceLogin } from './account' - -export interface TransactorService { - openRpc: (token: string, workspaceId: string) => Promise -} - -export interface TransactorRawApi extends Storage { - getModel: () => Promise - getAccount: () => Promise -} - -export async function createRpcClient ( - transactorService: TransactorService, - options: ConnectOptions -): Promise { - let token = options.workspaceToken - if (token === undefined) { - if (options.authOptions === undefined) { - throw new Error('Either workspaceToken or authOptions must be provided') - } - if (options.configUrl === '' && options.serverConfig === undefined) { - throw new Error('Either configUrl or serverConfig must be provided') - } - const ws = await getWorkspaceLogin(options.configUrl ?? '', options.authOptions, options.serverConfig) - token = ws.token - } - const client = new TransactorRpcClient(token, options.workspaceId ?? '', transactorService) - if (options.loadModel === true) { - await client.loadModel() - } - return client -} - -class TransactorRpcClient implements AccountClient { - private disposed = false - private model: ModelDb | undefined - private hierarchy: Hierarchy | undefined - private transactorRpcStub: TransactorRawApi | undefined - private account: Account | undefined - - constructor ( - private readonly token: string, - private readonly workspaceId: string, - private readonly transactorService: TransactorService - ) {} - - private async transactorStub (): Promise { - if (this.transactorRpcStub === undefined) { - this.transactorRpcStub = await this.transactorService.openRpc(this.token, this.workspaceId) - } - return this.transactorRpcStub - } - - async loadModel (): Promise { - const stub = await this.transactorStub() - const compressed = await stub.getModel() - const { model, hierarchy } = await decodeModel(compressed) - this.model = model - this.hierarchy = hierarchy - } - - notify (...tx: Tx[]): void { - // does nothing - } - - getHierarchy (): Hierarchy { - if (this.hierarchy === undefined) { - throw new Error('Hierarchy is not loaded, please use loadModel=true when initializing client') - } - return this.hierarchy - } - - getModel (): ModelDb { - if (this.model === undefined) { - throw new Error('Model is not loaded, please use loadModel=true when initializing client') - } - return this.model - } - - async findOne( - _class: Ref>, - query: DocumentQuery, - options?: FindOptions - ): Promise | undefined> { - return (await this.findAll(_class, query, options)).shift() - } - - async close (): Promise { - this.dispose() - } - - async findAll( - _class: Ref>, - query: DocumentQuery, - options?: FindOptions - ): Promise> { - const stub = await this.transactorStub() - return await stub.findAll(_class, query, options) - } - - async tx (tx: Tx): Promise { - const stub = await this.transactorStub() - return await stub.tx(tx) - } - - async searchFulltext (query: SearchQuery, options: SearchOptions): Promise { - // TODO - const result: SearchResult = { - docs: [], - total: 0 - } - return result - } - - async getAccount (): Promise { - if (this.account === undefined) { - const stub = await this.transactorStub() - this.account = await stub.getAccount() - } - return this.account - } - - private dispose (): void { - if (!this.disposed && this.transactorRpcStub !== undefined && Symbol.dispose in this.transactorRpcStub) { - this.disposed = true - ;(this.transactorRpcStub as any)[Symbol.dispose]() - } - } - - [Symbol.dispose] (): void { - this.dispose() - } -} diff --git a/workers/transactor-api/src/types.ts b/workers/transactor-api/src/types.ts deleted file mode 100644 index de0bac3009c..00000000000 --- a/workers/transactor-api/src/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright © 2024 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import { type AuthOptions, type ServerConfig } from './account' - -export interface ConnectOptions { - workspaceToken?: string - - workspaceId?: string - - loadModel?: boolean - - authOptions: AuthOptions - - configUrl?: string - - serverConfig?: ServerConfig -} diff --git a/workers/transactor-api/src/utils.ts b/workers/transactor-api/src/utils.ts deleted file mode 100644 index 452d28a3454..00000000000 --- a/workers/transactor-api/src/utils.ts +++ /dev/null @@ -1,96 +0,0 @@ -// -// Copyright © 2024 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import { gunzip } from 'zlib' -import { promisify } from 'util' -import { - Hierarchy, - ModelDb, - type MeasureContext, - type MeasureLogger, - type ParamsType, - type Tx -} from '@hcengineering/core' - -export async function unpackModel (compressed: Buffer | Uint8Array): Promise { - const ungzipAsync = promisify(gunzip) - const buffer = await ungzipAsync(new Uint8Array(compressed)) - const decoder = new TextDecoder() - const jsonString = decoder.decode(buffer) - const model = JSON.parse(jsonString) as Tx[] - return model -} - -export async function decodeModel (compressed: Buffer | Uint8Array): Promise<{ model: ModelDb, hierarchy: Hierarchy }> { - const txes = await unpackModel(compressed) - const hierarchy = new Hierarchy() - for (const tx of txes) { - hierarchy.tx(tx) - } - const model = new ModelDb(hierarchy) - const ctx = createDummyMeasureContext() - model.addTxes(ctx, txes, false) - return { model, hierarchy } -} - -function createConsoleLogger (): MeasureLogger { - return { - info: (message: string, obj?: Record) => { - console.info(message, obj) - }, - error: (message: string, obj?: Record) => { - console.error(message, obj) - }, - warn: (message: string, obj?: Record) => { - console.warn(message, obj) - }, - logOperation: (operation: string, time: number, params: ParamsType) => { - console.info(operation, { time, ...params }) - }, - close: async () => {} - } -} - -function createDummyMeasureContext (): MeasureContext { - const ctx: MeasureContext = { - id: '', - contextData: {}, - newChild: (name, params, fullParams, logger) => { - return ctx - }, - with: (name: any, params: any, op: any, fullParams: any) => { - return Promise.resolve() as Promise - }, - withSync: (name, params, op, fullParams) => { - return op(ctx) - }, - withLog: (name: any, params: any, op: any, fullParams: any) => { - return Promise.resolve() as Promise - }, - logger: createConsoleLogger(), - measure: (name, value, override) => {}, - error: (message, args) => { - console.error(message, args) - }, - info: (message, args) => { - console.info(message, args) - }, - warn: (message, args) => { - console.warn(message, args) - }, - end: () => {} - } - return ctx -} diff --git a/workers/transactor-api/tsconfig.json b/workers/transactor-api/tsconfig.json deleted file mode 100644 index f017cc597c2..00000000000 --- a/workers/transactor-api/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./node_modules/@hcengineering/platform-rig/profiles/node/tsconfig.json", - - "compilerOptions": { - "rootDir": "./src", - "outDir": "./lib", - "declarationDir": "./types", - "tsBuildInfoFile": ".build/build.tsbuildinfo" - } -} \ No newline at end of file diff --git a/workers/transactor-http-api/.eslintrc.js b/workers/transactor-http-api/.eslintrc.js deleted file mode 100644 index 72235dc2833..00000000000 --- a/workers/transactor-http-api/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'], - parserOptions: { - tsconfigRootDir: __dirname, - project: './tsconfig.json' - } -} diff --git a/workers/transactor-http-api/config/rig.json b/workers/transactor-http-api/config/rig.json deleted file mode 100644 index 0110930f55e..00000000000 --- a/workers/transactor-http-api/config/rig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@hcengineering/platform-rig" -} diff --git a/workers/transactor-http-api/jest.config.js b/workers/transactor-http-api/jest.config.js deleted file mode 100644 index 2cfd408b679..00000000000 --- a/workers/transactor-http-api/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], - roots: ["./src"], - coverageReporters: ["text-summary", "html"] -} diff --git a/workers/transactor-http-api/package.json b/workers/transactor-http-api/package.json deleted file mode 100644 index e8a0820524e..00000000000 --- a/workers/transactor-http-api/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "@hcengineering/cloud-transactor-http-api", - "version": "0.6.0", - "main": "lib/index.js", - "types": "types/index.d.ts", - "template": "cloud", - "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev --port 4040", - "dev-local": "wrangler dev --port 4040 --local --upstream-protocol=http", - "start": "wrangler dev --port 4040", - "cf-typegen": "wrangler types", - "build": "compile", - "build:watch": "compile", - "test": "jest --passWithNoTests --silent --forceExit", - "format": "format src", - "_phase:build": "compile transpile src", - "_phase:test": "jest --passWithNoTests --silent --forceExit", - "_phase:format": "format src", - "_phase:validate": "compile validate" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20241022.0", - "@hcengineering/platform-rig": "^0.6.0", - "@types/jest": "^29.5.5", - "@types/node": "~20.11.16", - "@typescript-eslint/eslint-plugin": "^6.11.0", - "@typescript-eslint/parser": "^6.11.0", - "eslint-config-standard-with-typescript": "^40.0.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-n": "^15.4.0", - "eslint": "^8.54.0", - "jest": "^29.7.0", - "prettier": "^3.1.0", - "ts-jest": "^29.1.1", - "typescript": "^5.3.3", - "wrangler": "^3.97.0" - }, - "dependencies": { - "@hcengineering/analytics": "^0.6.0", - "@hcengineering/core": "^0.6.32", - "itty-router": "^5.0.18" - } -} diff --git a/workers/transactor-http-api/src/index.ts b/workers/transactor-http-api/src/index.ts deleted file mode 100644 index 709c3935a98..00000000000 --- a/workers/transactor-http-api/src/index.ts +++ /dev/null @@ -1,125 +0,0 @@ -// -// Copyright © 2024 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import { type Account, type Storage } from '@hcengineering/core' -import { WorkerEntrypoint } from 'cloudflare:workers' -import { type IRequest, type IRequestStrict, type RequestHandler, Router, cors, error, html, json } from 'itty-router' - -const { preflight, corsify } = cors({ - maxAge: 86400 -}) - -const router = Router({ - before: [preflight], - finally: [corsify] -}) - -type WorkspaceRequest = { - workspaceId: string - token: string -} & IRequestStrict - -interface TransactorService { - openRpc: (token: string, workspaceId: string) => Promise -} - -interface TransactorRawApi extends Storage { - getModel: () => Promise - getAccount: () => Promise -} - -const withWorkspace: RequestHandler = (request: WorkspaceRequest) => { - if (request.params.workspaceId === undefined || request.params.workspaceId === '') { - return error(400, 'Missing workspace') - } - const token = request.headers.get('Authorization') - if (token === null) { - return error(401, 'Missing Authorization header') - } - request.workspaceId = decodeURIComponent(request.params.workspaceId) - request.token = token.split(' ')[1] -} - -async function callTransactor ( - request: WorkspaceRequest, - env: Env, - method: (transactorRpc: TransactorRawApi) => Promise -): Promise { - const transactorService = env.TRANSACTOR_SERVICE as any as TransactorService - const transactorRpc = await transactorService.openRpc(request.token, request.workspaceId) - try { - return await method(transactorRpc) - } finally { - if (Symbol.dispose in transactorRpc) { - ;(transactorRpc as any)[Symbol.dispose]() - } - } -} - -router.post('/find-all/:workspaceId', withWorkspace, async (request: WorkspaceRequest, env: Env) => { - return await callTransactor(request, env, async (transactorRpc) => { - if (request.body === null) { - return error(400, 'Missing body') - } - const { _class, query, options }: any = await request.json() - const result = await transactorRpc.findAll(_class, query, options) - return json(result) - }) -}) - -router.post('/tx/:workspaceId', withWorkspace, async (request: WorkspaceRequest, env: Env) => { - return await callTransactor(request, env, async (transactorRpc) => { - if (request.body === null) { - return error(400, 'Missing body') - } - const tx: any = await request.json() - const result = await transactorRpc.tx(tx) - return json(result) - }) -}) - -router.get('/model/:workspaceId', withWorkspace, async (request: WorkspaceRequest, env: Env) => { - return await callTransactor(request, env, async (transactorRpc) => { - const model = await transactorRpc.getModel() - return new Response(model, { - headers: { - 'Content-Type': 'application/octet-stream', - 'Content-Length': model.length.toString() - } - }) - }) -}) - -router.get('/account/:workspaceId', withWorkspace, async (request: WorkspaceRequest, env: Env) => { - return await callTransactor(request, env, async (transactorRpc) => { - const account = await transactorRpc.getAccount() - return json(account) - }) -}) - -router.all('/', () => - html( - `Huly® Transactor API™ https://huly.io - © 2024 Huly Labs` - ) -) - -router.all('*', () => error(404)) - -export default class TransactorHttpApiWorker extends WorkerEntrypoint { - async fetch (request: IRequest): Promise { - return await router.fetch(request, this.env, this.ctx).catch(error) - } -} diff --git a/workers/transactor-http-api/tsconfig.json b/workers/transactor-http-api/tsconfig.json deleted file mode 100644 index f16d7ab53b8..00000000000 --- a/workers/transactor-http-api/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json", - - "compilerOptions": { - "rootDir": "./src", - "outDir": "./lib", - "declarationDir": "./types", - "tsBuildInfoFile": ".build/build.tsbuildinfo", - "types": ["@cloudflare/workers-types", "node", "jest"] - } -} diff --git a/workers/transactor-http-api/worker-configuration.d.ts b/workers/transactor-http-api/worker-configuration.d.ts deleted file mode 100644 index 9ec112dfd62..00000000000 --- a/workers/transactor-http-api/worker-configuration.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Generated by Wrangler by running `wrangler types` - -interface Env { - TRANSACTOR_SERVICE: Fetcher; -} diff --git a/workers/transactor-http-api/wrangler.toml b/workers/transactor-http-api/wrangler.toml deleted file mode 100644 index ba5170491cb..00000000000 --- a/workers/transactor-http-api/wrangler.toml +++ /dev/null @@ -1,11 +0,0 @@ -#:schema node_modules/wrangler/config-schema.json -name = "cloud-transactor-http-api" -main = "src/index.ts" -compatibility_date = "2024-09-23" -compatibility_flags = ["nodejs_compat"] -keep_vars = true - -[[services]] -binding = "TRANSACTOR_SERVICE" -service = "cloud-transactor" -entrypoint = "TransactorRpc"