diff --git a/.github/workflows/buildtest.yml b/.github/workflows/buildtest.yml
new file mode 100644
index 000000000..90eddbbfd
--- /dev/null
+++ b/.github/workflows/buildtest.yml
@@ -0,0 +1,23 @@
+name: Test Builds
+
+on:
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [20.x] # Specify node versions you want to test against
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 2 # Checkout HEAD^
+
+ - name: Build packages
+ run: docker compose --profile building build
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 42307bfac..8c4dd3509 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -50,7 +50,7 @@ jobs:
run: pnpm install
- name: Use Turbo to build affected packages
- run: pnpm turbo build --cache-dir=.turbo --filter="./packages/*"
+ run: pnpm turbo build --cache-dir=.turbo --filter="./packages/**"
- name: Prettier
run: pnpm prettier:check
diff --git a/.gitignore b/.gitignore
index 005843d7e..2c9817b77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ node_modules
# Local env files
.env
.env.local
+.env.development
.env.development.local
.env.test.local
.env.production.local
diff --git a/apps/gateway/package.json b/apps/gateway/package.json
index 1d4baba7f..53f7e38b3 100644
--- a/apps/gateway/package.json
+++ b/apps/gateway/package.json
@@ -21,6 +21,7 @@
"@latitude-data/core": "workspace:^",
"@latitude-data/env": "workspace:^",
"@latitude-data/jobs": "workspace:^",
+ "@latitude-data/mailers": "workspace:^",
"@t3-oss/env-core": "^0.10.1",
"drizzle-orm": "^0.33.0",
"hono": "^4.5.3",
diff --git a/apps/gateway/src/common/pipeToStream.ts b/apps/gateway/src/common/pipeToStream.ts
index a87f3c941..6e0bcec06 100644
--- a/apps/gateway/src/common/pipeToStream.ts
+++ b/apps/gateway/src/common/pipeToStream.ts
@@ -1,4 +1,4 @@
-import { streamToGenerator } from '@latitude-data/core'
+import { streamToGenerator } from '@latitude-data/core/lib/streamToGenerator'
import { SSEStreamingApi } from 'hono/streaming'
export async function pipeToStream(
diff --git a/apps/gateway/src/middlewares/auth.ts b/apps/gateway/src/middlewares/auth.ts
index f2dfdf694..edee22d7f 100644
--- a/apps/gateway/src/middlewares/auth.ts
+++ b/apps/gateway/src/middlewares/auth.ts
@@ -1,8 +1,8 @@
+import type { ApiKey, Workspace } from '@latitude-data/core/browser'
import {
unsafelyFindWorkspace,
unsafelyGetApiKeyByToken,
-} from '@latitude-data/core'
-import type { ApiKey, Workspace } from '@latitude-data/core/browser'
+} from '@latitude-data/core/data-access'
import { bearerAuth } from 'hono/bearer-auth'
declare module 'hono' {
diff --git a/apps/gateway/src/middlewares/errorHandler.ts b/apps/gateway/src/middlewares/errorHandler.ts
index 7cc836f33..cb70d7b9e 100644
--- a/apps/gateway/src/middlewares/errorHandler.ts
+++ b/apps/gateway/src/middlewares/errorHandler.ts
@@ -1,4 +1,7 @@
-import { LatitudeError, UnprocessableEntityError } from '@latitude-data/core'
+import {
+ LatitudeError,
+ UnprocessableEntityError,
+} from '@latitude-data/core/lib/errors'
import { createMiddleware } from 'hono/factory'
import HttpStatusCodes from '../common/httpStatusCodes'
diff --git a/apps/gateway/src/routes/api/v1/chats/handlers/addMessage.test.ts b/apps/gateway/src/routes/api/v1/chats/handlers/addMessage.test.ts
index 6b1d91452..ee3c58727 100644
--- a/apps/gateway/src/routes/api/v1/chats/handlers/addMessage.test.ts
+++ b/apps/gateway/src/routes/api/v1/chats/handlers/addMessage.test.ts
@@ -1,4 +1,3 @@
-import { apiKeys, database, Result } from '@latitude-data/core'
import {
ApiKey,
ChainEventTypes,
@@ -6,7 +5,10 @@ import {
StreamEventTypes,
Workspace,
} from '@latitude-data/core/browser'
+import { database } from '@latitude-data/core/client'
import { createProject } from '@latitude-data/core/factories'
+import { Result } from '@latitude-data/core/lib/Result'
+import { apiKeys } from '@latitude-data/core/schema'
import app from '$/routes/app'
import { eq } from 'drizzle-orm'
import { testConsumeStream } from 'test/helpers'
@@ -48,14 +50,17 @@ const mocks = vi.hoisted(() => ({
},
}))
-vi.mock('@latitude-data/core', async (importOriginal) => {
- const original = (await importOriginal()) as typeof importOriginal
+vi.mock(
+ '@latitude-data/core/services/documentLogs/index',
+ async (importOriginal) => {
+ const original = (await importOriginal()) as typeof importOriginal
- return {
- ...original,
- addMessages: mocks.addMessages,
- }
-})
+ return {
+ ...original,
+ addMessages: mocks.addMessages,
+ }
+ },
+)
vi.mock('$/jobs', () => ({
queues: mocks.queues,
@@ -90,7 +95,6 @@ describe('POST /add-message', () => {
workspace = wsp
// TODO: move to core
const key = await database.query.apiKeys.findFirst({
- // @ts-ignore
where: eq(apiKeys.workspaceId, workspace.id),
})
apiKey = key!
diff --git a/apps/gateway/src/routes/api/v1/chats/handlers/addMessage.ts b/apps/gateway/src/routes/api/v1/chats/handlers/addMessage.ts
index 953274c5a..8fdc34d98 100644
--- a/apps/gateway/src/routes/api/v1/chats/handlers/addMessage.ts
+++ b/apps/gateway/src/routes/api/v1/chats/handlers/addMessage.ts
@@ -1,5 +1,6 @@
import { zValidator } from '@hono/zod-validator'
-import { addMessages, LogSources } from '@latitude-data/core'
+import { LogSources } from '@latitude-data/core/browser'
+import { addMessages } from '@latitude-data/core/services/documentLogs/index'
import { messageSchema } from '$/common/messageSchema'
import { pipeToStream } from '$/common/pipeToStream'
import { queues } from '$/jobs'
diff --git a/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/_shared.ts b/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/_shared.ts
index 6dc21f921..5d51b08a2 100644
--- a/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/_shared.ts
+++ b/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/_shared.ts
@@ -1,9 +1,9 @@
+import type { Workspace } from '@latitude-data/core/browser'
import {
CommitsRepository,
DocumentVersionsRepository,
ProjectsRepository,
-} from '@latitude-data/core'
-import type { Workspace } from '@latitude-data/core/browser'
+} from '@latitude-data/core/repositories'
export const getData = async ({
workspace,
diff --git a/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/get.test.ts b/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/get.test.ts
index 6b218c747..9a6ed4096 100644
--- a/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/get.test.ts
+++ b/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/get.test.ts
@@ -1,14 +1,12 @@
-import {
- apiKeys,
- database,
- DocumentVersionsRepository,
- mergeCommit,
-} from '@latitude-data/core'
+import { database } from '@latitude-data/core/client'
import {
createDocumentVersion,
createDraft,
createProject,
} from '@latitude-data/core/factories'
+import { DocumentVersionsRepository } from '@latitude-data/core/repositories'
+import { apiKeys } from '@latitude-data/core/schema'
+import { mergeCommit } from '@latitude-data/core/services/commits/merge'
import app from '$/routes/app'
import { eq } from 'drizzle-orm'
import { describe, expect, it, vi } from 'vitest'
diff --git a/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/run.test.ts b/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/run.test.ts
index d2b0ac408..826bd3535 100644
--- a/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/run.test.ts
+++ b/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/run.test.ts
@@ -1,4 +1,3 @@
-import { apiKeys, database, mergeCommit, Result } from '@latitude-data/core'
import {
ChainEventTypes,
Commit,
@@ -7,11 +6,15 @@ import {
StreamEventTypes,
Workspace,
} from '@latitude-data/core/browser'
+import { database } from '@latitude-data/core/client'
import {
createDocumentVersion,
createDraft,
createProject,
} from '@latitude-data/core/factories'
+import { Result } from '@latitude-data/core/lib/Result'
+import { apiKeys } from '@latitude-data/core/schema'
+import { mergeCommit } from '@latitude-data/core/services/commits/merge'
import app from '$/routes/app'
import { eq } from 'drizzle-orm'
import { testConsumeStream } from 'test/helpers'
@@ -29,14 +32,17 @@ const mocks = vi.hoisted(() => ({
},
}))
-vi.mock('@latitude-data/core', async (importOriginal) => {
- const original = (await importOriginal()) as typeof importOriginal
+vi.mock(
+ '@latitude-data/core/services/commits/runDocumentAtCommit',
+ async (importOriginal) => {
+ const original = (await importOriginal()) as typeof importOriginal
- return {
- ...original,
- runDocumentAtCommit: mocks.runDocumentAtCommit,
- }
-})
+ return {
+ ...original,
+ runDocumentAtCommit: mocks.runDocumentAtCommit,
+ }
+ },
+)
vi.mock('$/jobs', () => ({
queues: mocks.queues,
diff --git a/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/run.ts b/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/run.ts
index d445571ee..0587cd265 100644
--- a/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/run.ts
+++ b/apps/gateway/src/routes/api/v1/projects/:projectId/commits/:commitUuid/documents/handlers/run.ts
@@ -1,5 +1,6 @@
import { zValidator } from '@hono/zod-validator'
-import { LogSources, runDocumentAtCommit } from '@latitude-data/core'
+import { LogSources } from '@latitude-data/core/browser'
+import { runDocumentAtCommit } from '@latitude-data/core/services/commits/runDocumentAtCommit'
import { pipeToStream } from '$/common/pipeToStream'
import { queues } from '$/jobs'
import { Factory } from 'hono/factory'
@@ -41,8 +42,7 @@ export const runHandler = factory.createHandlers(
commit,
parameters,
providerLogHandler: (log) => {
- // TODO: review why this is possibly undefined now
- queues.defaultQueue.jobs.enqueueCreateProviderLogJob!({
+ queues.defaultQueue.jobs.enqueueCreateProviderLogJob({
...log,
source,
apiKeyId: apiKey.id,
@@ -52,8 +52,7 @@ export const runHandler = factory.createHandlers(
await pipeToStream(stream, result.stream)
- // TODO: review why this is possibly undefined now
- queues.defaultQueue.jobs.enqueueCreateDocumentLogJob!({
+ queues.defaultQueue.jobs.enqueueCreateDocumentLogJob({
commit,
data: {
uuid: result.documentLogUuid,
diff --git a/apps/gateway/tsconfig.json b/apps/gateway/tsconfig.json
index 59a128fcf..7cfca447d 100644
--- a/apps/gateway/tsconfig.json
+++ b/apps/gateway/tsconfig.json
@@ -9,8 +9,13 @@
"$/*": ["./src/*"],
"acorn": ["node_modules/@latitude-data/typescript-config/types/acorn"]
},
- "types": ["node"]
+ "types": ["node"],
+ "jsx": "preserve"
},
- "include": ["src/**/*.ts", "test/**/*.ts"],
+ "include": [
+ "src/**/*.ts",
+ "src/**/*.test.ts",
+ "test/**/*.ts"
+ ],
"exclude": ["node_modules"]
}
diff --git a/apps/gateway/tsup.config.ts b/apps/gateway/tsup.config.ts
index e97a65809..50bcd90d7 100644
--- a/apps/gateway/tsup.config.ts
+++ b/apps/gateway/tsup.config.ts
@@ -27,5 +27,6 @@ export default defineConfig({
'@latitude-data/env',
'@latitude-data/core',
'@latitude-data/jobs',
+ '@latitude-data/mailers',
],
})
diff --git a/apps/web/package.json b/apps/web/package.json
index e86090d11..fd472c514 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -19,6 +19,7 @@
"@latitude-data/core": "workspace:*",
"@latitude-data/env": "workspace:^",
"@latitude-data/jobs": "workspace:*",
+ "@latitude-data/mailers": "workspace:^",
"@latitude-data/sdk-js": "workspace:*",
"@latitude-data/web-ui": "workspace:*",
"@lucia-auth/adapter-drizzle": "^1.0.7",
diff --git a/apps/web/public/logodark.png b/apps/web/public/logodark.png
new file mode 100644
index 000000000..70988345c
Binary files /dev/null and b/apps/web/public/logodark.png differ
diff --git a/apps/web/src/actions/commits/create.ts b/apps/web/src/actions/commits/create.ts
index 1f5c5a460..3d303aeed 100644
--- a/apps/web/src/actions/commits/create.ts
+++ b/apps/web/src/actions/commits/create.ts
@@ -1,6 +1,6 @@
'use server'
-import { createCommit } from '@latitude-data/core'
+import { createCommit } from '@latitude-data/core/services/commits/create'
import { z } from 'zod'
import { withProject } from '../procedures'
diff --git a/apps/web/src/actions/commits/deleteDraftCommit.test.ts b/apps/web/src/actions/commits/deleteDraftCommit.test.ts
index 751c13125..9329597ce 100644
--- a/apps/web/src/actions/commits/deleteDraftCommit.test.ts
+++ b/apps/web/src/actions/commits/deleteDraftCommit.test.ts
@@ -1,10 +1,10 @@
-import { database } from '@latitude-data/core'
import type {
Commit,
Project,
SafeUser,
Workspace,
} from '@latitude-data/core/browser'
+import { database } from '@latitude-data/core/client'
import * as factories from '@latitude-data/core/factories'
import { deleteDraftCommitAction } from '$/actions/commits/deleteDraftCommitAction'
import { beforeEach, describe, expect, it, vi } from 'vitest'
diff --git a/apps/web/src/actions/commits/deleteDraftCommitAction.ts b/apps/web/src/actions/commits/deleteDraftCommitAction.ts
index 53cb14af9..fa3874bb3 100644
--- a/apps/web/src/actions/commits/deleteDraftCommitAction.ts
+++ b/apps/web/src/actions/commits/deleteDraftCommitAction.ts
@@ -1,6 +1,7 @@
'use server'
-import { CommitsRepository, deleteCommitDraft } from '@latitude-data/core'
+import { CommitsRepository } from '@latitude-data/core/repositories'
+import { deleteCommitDraft } from '@latitude-data/core/services/commits/delete'
import { z } from 'zod'
import { withProject } from '../procedures'
diff --git a/apps/web/src/actions/commits/fetchCommitsByProjectAction.test.ts b/apps/web/src/actions/commits/fetchCommitsByProjectAction.test.ts
index 90d56e30d..592208ded 100644
--- a/apps/web/src/actions/commits/fetchCommitsByProjectAction.test.ts
+++ b/apps/web/src/actions/commits/fetchCommitsByProjectAction.test.ts
@@ -1,5 +1,4 @@
-import { CommitStatus } from '@latitude-data/core'
-import type { Workspace } from '@latitude-data/core/browser'
+import { CommitStatus, type Workspace } from '@latitude-data/core/browser'
import * as factories from '@latitude-data/core/factories'
import { beforeEach, describe, expect, it, vi } from 'vitest'
diff --git a/apps/web/src/actions/commits/fetchCommitsByProjectAction.ts b/apps/web/src/actions/commits/fetchCommitsByProjectAction.ts
index 194c7a8bd..14991a442 100644
--- a/apps/web/src/actions/commits/fetchCommitsByProjectAction.ts
+++ b/apps/web/src/actions/commits/fetchCommitsByProjectAction.ts
@@ -1,6 +1,7 @@
'use server'
-import { CommitsRepository, CommitStatus } from '@latitude-data/core'
+import { CommitStatus } from '@latitude-data/core/browser'
+import { CommitsRepository } from '@latitude-data/core/repositories'
import { z } from 'zod'
import { withProject } from '../procedures'
diff --git a/apps/web/src/actions/commits/getChangedDocumentsInDraftAction.ts b/apps/web/src/actions/commits/getChangedDocumentsInDraftAction.ts
index 0b1dd00cd..6032829a9 100644
--- a/apps/web/src/actions/commits/getChangedDocumentsInDraftAction.ts
+++ b/apps/web/src/actions/commits/getChangedDocumentsInDraftAction.ts
@@ -1,6 +1,6 @@
'use server'
-import { CommitsRepository } from '@latitude-data/core'
+import { CommitsRepository } from '@latitude-data/core/repositories'
import { z } from 'zod'
import { withProject } from '../procedures'
diff --git a/apps/web/src/actions/commits/publishDraftCommit.test.ts b/apps/web/src/actions/commits/publishDraftCommit.test.ts
index 2b1334b30..62fcab963 100644
--- a/apps/web/src/actions/commits/publishDraftCommit.test.ts
+++ b/apps/web/src/actions/commits/publishDraftCommit.test.ts
@@ -1,4 +1,3 @@
-import { updateDocument } from '@latitude-data/core'
import type {
Commit,
DocumentVersion,
@@ -7,6 +6,7 @@ import type {
Workspace,
} from '@latitude-data/core/browser'
import * as factories from '@latitude-data/core/factories'
+import { updateDocument } from '@latitude-data/core/services/documents/update'
import { publishDraftCommitAction } from '$/actions/commits/publishDraftCommitAction'
import { beforeEach, describe, expect, it, vi } from 'vitest'
diff --git a/apps/web/src/actions/commits/publishDraftCommitAction.ts b/apps/web/src/actions/commits/publishDraftCommitAction.ts
index 70ffc2810..60cedc534 100644
--- a/apps/web/src/actions/commits/publishDraftCommitAction.ts
+++ b/apps/web/src/actions/commits/publishDraftCommitAction.ts
@@ -1,6 +1,7 @@
'use server'
-import { CommitsRepository, mergeCommit } from '@latitude-data/core'
+import { CommitsRepository } from '@latitude-data/core/repositories'
+import { mergeCommit } from '@latitude-data/core/services/commits/merge'
import { z } from 'zod'
import { withProject } from '../procedures'
diff --git a/apps/web/src/actions/documents/create.ts b/apps/web/src/actions/documents/create.ts
index f8b7fae74..24d9a2907 100644
--- a/apps/web/src/actions/documents/create.ts
+++ b/apps/web/src/actions/documents/create.ts
@@ -1,6 +1,7 @@
'use server'
-import { CommitsRepository, createNewDocument } from '@latitude-data/core'
+import { CommitsRepository } from '@latitude-data/core/repositories'
+import { createNewDocument } from '@latitude-data/core/services/documents/create'
import { z } from 'zod'
import { withProject } from '../procedures'
diff --git a/apps/web/src/actions/documents/destroyDocumentAction/index.test.ts b/apps/web/src/actions/documents/destroyDocumentAction/index.test.ts
index 0dd53ed48..03a0bd513 100644
--- a/apps/web/src/actions/documents/destroyDocumentAction/index.test.ts
+++ b/apps/web/src/actions/documents/destroyDocumentAction/index.test.ts
@@ -1,11 +1,12 @@
-import { database, documentVersions } from '@latitude-data/core'
import {
Commit,
DocumentVersion,
Project,
SafeUser,
} from '@latitude-data/core/browser'
+import { database } from '@latitude-data/core/client'
import { createDraft, createProject } from '@latitude-data/core/factories'
+import { documentVersions } from '@latitude-data/core/schema'
import { and, eq } from 'drizzle-orm'
import { beforeEach, describe, expect, it, vi } from 'vitest'
@@ -94,6 +95,7 @@ describe('destroyDocumentAction', async () => {
})
// TODO: move to core
const documents = await database.query.documentVersions.findMany({
+ // @ts-ignore
where: and(eq(documentVersions.documentUuid, document.documentUuid)),
})
diff --git a/apps/web/src/actions/documents/destroyDocumentAction/index.ts b/apps/web/src/actions/documents/destroyDocumentAction/index.ts
index 2fc3c749d..a6dc7db14 100644
--- a/apps/web/src/actions/documents/destroyDocumentAction/index.ts
+++ b/apps/web/src/actions/documents/destroyDocumentAction/index.ts
@@ -2,9 +2,9 @@
import {
CommitsRepository,
- destroyDocument,
DocumentVersionsRepository,
-} from '@latitude-data/core'
+} from '@latitude-data/core/repositories'
+import { destroyDocument } from '@latitude-data/core/services/documents/destroyDocument'
import { withProject } from '$/actions/procedures'
import { z } from 'zod'
diff --git a/apps/web/src/actions/documents/destroyFolderAction.ts b/apps/web/src/actions/documents/destroyFolderAction.ts
index 07c433683..2b349fd4d 100644
--- a/apps/web/src/actions/documents/destroyFolderAction.ts
+++ b/apps/web/src/actions/documents/destroyFolderAction.ts
@@ -1,6 +1,7 @@
'use server'
-import { CommitsRepository, destroyFolder } from '@latitude-data/core'
+import { CommitsRepository } from '@latitude-data/core/repositories'
+import { destroyFolder } from '@latitude-data/core/services/documents/destroyFolder'
import { z } from 'zod'
import { withProject } from '../procedures'
diff --git a/apps/web/src/actions/documents/getDocumentsAtCommitAction.ts b/apps/web/src/actions/documents/getDocumentsAtCommitAction.ts
index 408cc6d80..3c2b2d878 100644
--- a/apps/web/src/actions/documents/getDocumentsAtCommitAction.ts
+++ b/apps/web/src/actions/documents/getDocumentsAtCommitAction.ts
@@ -3,7 +3,7 @@
import {
CommitsRepository,
DocumentVersionsRepository,
-} from '@latitude-data/core'
+} from '@latitude-data/core/repositories'
import { z } from 'zod'
import { withProject } from '../procedures'
diff --git a/apps/web/src/actions/documents/updateContent.test.ts b/apps/web/src/actions/documents/updateContent.test.ts
index 7ab209b47..b7e1340ca 100644
--- a/apps/web/src/actions/documents/updateContent.test.ts
+++ b/apps/web/src/actions/documents/updateContent.test.ts
@@ -1,6 +1,6 @@
-import { updateDocument } from '@latitude-data/core'
import { DocumentVersion, Project, SafeUser } from '@latitude-data/core/browser'
import * as factories from '@latitude-data/core/factories'
+import { updateDocument } from '@latitude-data/core/services/documents/update'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { updateDocumentContentAction } from './updateContent'
diff --git a/apps/web/src/actions/documents/updateContent.ts b/apps/web/src/actions/documents/updateContent.ts
index 7ad75f4a3..e508a8bfc 100644
--- a/apps/web/src/actions/documents/updateContent.ts
+++ b/apps/web/src/actions/documents/updateContent.ts
@@ -3,8 +3,8 @@
import {
CommitsRepository,
DocumentVersionsRepository,
- updateDocument,
-} from '@latitude-data/core'
+} from '@latitude-data/core/repositories'
+import { updateDocument } from '@latitude-data/core/services/documents/update'
import { z } from 'zod'
import { withProject } from '../procedures'
diff --git a/apps/web/src/actions/invitations/accept.ts b/apps/web/src/actions/invitations/accept.ts
new file mode 100644
index 000000000..0b9f8826c
--- /dev/null
+++ b/apps/web/src/actions/invitations/accept.ts
@@ -0,0 +1,46 @@
+'use server'
+
+import {
+ unsafelyFindMembershipByToken,
+ unsafelyFindWorkspace,
+ unsafelyGetUser,
+} from '@latitude-data/core/data-access'
+import { NotFoundError } from '@latitude-data/core/lib/errors'
+import { acceptInvitation } from '@latitude-data/core/services/invitations/accept'
+import { setSession } from '$/services/auth/setSession'
+import { ROUTES } from '$/services/routes'
+import { redirect } from 'next/navigation'
+import { z } from 'zod'
+import { createServerAction } from 'zsa'
+
+export const acceptInvitationAction = createServerAction()
+ .input(
+ z.object({
+ membershipToken: z.string(),
+ email: z.string().optional(),
+ }),
+ { type: 'formData' },
+ )
+ .handler(async ({ input }) => {
+ const { membershipToken } = input
+ const membership = await unsafelyFindMembershipByToken(
+ membershipToken,
+ ).then((r) => r.unwrap())
+ const workspace = await unsafelyFindWorkspace(membership.workspaceId).then(
+ (r) => r.unwrap(),
+ )
+
+ const user = await unsafelyGetUser(membership.userId)
+ if (!user) throw new NotFoundError('User not found')
+
+ await acceptInvitation({ membership, user })
+
+ setSession({
+ sessionData: {
+ user,
+ workspace: { id: Number(workspace.id), name: workspace.name },
+ },
+ })
+
+ return redirect(ROUTES.root)
+ })
diff --git a/apps/web/src/actions/magicLinkTokens/confirm.ts b/apps/web/src/actions/magicLinkTokens/confirm.ts
new file mode 100644
index 000000000..fed6a4683
--- /dev/null
+++ b/apps/web/src/actions/magicLinkTokens/confirm.ts
@@ -0,0 +1,39 @@
+'use server'
+
+import { unsafelyGetUser } from '@latitude-data/core/data-access'
+import { NotFoundError } from '@latitude-data/core/lib/errors'
+import { confirmMagicLinkToken } from '@latitude-data/core/services/magicLinkTokens/confirm'
+import { getFirstWorkspace } from '$/data-access'
+import { setSession } from '$/services/auth/setSession'
+import { ROUTES } from '$/services/routes'
+import { redirect } from 'next/navigation'
+import { z } from 'zod'
+import { createServerAction } from 'zsa'
+
+export const confirmMagicLinkTokenAction = createServerAction()
+ .input(
+ z.object({
+ token: z.string(),
+ }),
+ )
+ .handler(async ({ input }) => {
+ const magicLinkToken = await confirmMagicLinkToken(input.token).then((r) =>
+ r.unwrap(),
+ )
+
+ const user = await unsafelyGetUser(magicLinkToken.userId)
+ if (!user) throw new NotFoundError('User not found')
+
+ const workspace = await getFirstWorkspace({ userId: user.id }).then((r) =>
+ r.unwrap(),
+ )
+
+ setSession({
+ sessionData: {
+ user,
+ workspace,
+ },
+ })
+
+ redirect(ROUTES.root)
+ })
diff --git a/apps/web/src/actions/memberships/destroy.ts b/apps/web/src/actions/memberships/destroy.ts
new file mode 100644
index 000000000..447a21a47
--- /dev/null
+++ b/apps/web/src/actions/memberships/destroy.ts
@@ -0,0 +1,24 @@
+'use server'
+
+import { MembershipsRepository } from '@latitude-data/core/repositories'
+import { destroyMembership } from '@latitude-data/core/services/memberships/destroy'
+import { z } from 'zod'
+
+import { authProcedure } from '../procedures'
+
+export const destroyMembershipAction = authProcedure
+ .createServerAction()
+ .input(
+ z.object({
+ userId: z.string(),
+ }),
+ )
+ .handler(async ({ input, ctx }) => {
+ const { userId } = input
+ const membershipsScope = new MembershipsRepository(ctx.workspace.id)
+ const membership = await membershipsScope
+ .findByUserId(userId)
+ .then((r) => r.unwrap())
+
+ return await destroyMembership(membership).then((r) => r.unwrap())
+ })
diff --git a/apps/web/src/actions/procedures/index.ts b/apps/web/src/actions/procedures/index.ts
index 9e75e0b85..966d57a3f 100644
--- a/apps/web/src/actions/procedures/index.ts
+++ b/apps/web/src/actions/procedures/index.ts
@@ -1,4 +1,5 @@
-import { ProjectsRepository, UnauthorizedError } from '@latitude-data/core'
+import { UnauthorizedError } from '@latitude-data/core/lib/errors'
+import { ProjectsRepository } from '@latitude-data/core/repositories'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { z } from 'zod'
import { createServerActionProcedure } from 'zsa'
diff --git a/apps/web/src/actions/providerApiKeys/create.ts b/apps/web/src/actions/providerApiKeys/create.ts
index 8526e0fdb..181994b88 100644
--- a/apps/web/src/actions/providerApiKeys/create.ts
+++ b/apps/web/src/actions/providerApiKeys/create.ts
@@ -1,7 +1,7 @@
'use server'
-import { createProviderApiKey } from '@latitude-data/core'
import { Providers } from '@latitude-data/core/browser'
+import { createProviderApiKey } from '@latitude-data/core/services/providerApiKeys/create'
import providerApiKeyPresenter from '$/presenters/providerApiKeyPresenter'
import { z } from 'zod'
diff --git a/apps/web/src/actions/providerApiKeys/destroy.ts b/apps/web/src/actions/providerApiKeys/destroy.ts
index 08e30b439..c8eb4468d 100644
--- a/apps/web/src/actions/providerApiKeys/destroy.ts
+++ b/apps/web/src/actions/providerApiKeys/destroy.ts
@@ -1,9 +1,7 @@
'use server'
-import {
- destroyProviderApiKey,
- ProviderApiKeysRepository,
-} from '@latitude-data/core'
+import { ProviderApiKeysRepository } from '@latitude-data/core/repositories'
+import { destroyProviderApiKey } from '@latitude-data/core/services/providerApiKeys/destroy'
import providerApiKeyPresenter from '$/presenters/providerApiKeyPresenter'
import { z } from 'zod'
diff --git a/apps/web/src/actions/providerApiKeys/fetch.test.ts b/apps/web/src/actions/providerApiKeys/fetch.test.ts
index 6add1d0f6..d1630538f 100644
--- a/apps/web/src/actions/providerApiKeys/fetch.test.ts
+++ b/apps/web/src/actions/providerApiKeys/fetch.test.ts
@@ -1,6 +1,6 @@
-import { createProviderApiKey } from '@latitude-data/core'
import { Providers } from '@latitude-data/core/browser'
import { createWorkspace } from '@latitude-data/core/factories'
+import { createProviderApiKey } from '@latitude-data/core/services/providerApiKeys/create'
import { getSession } from '$/services/auth/getSession'
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest'
diff --git a/apps/web/src/actions/providerApiKeys/fetch.ts b/apps/web/src/actions/providerApiKeys/fetch.ts
index 7c15b306e..ff74e3c9d 100644
--- a/apps/web/src/actions/providerApiKeys/fetch.ts
+++ b/apps/web/src/actions/providerApiKeys/fetch.ts
@@ -1,6 +1,6 @@
'use server'
-import { ProviderApiKeysRepository } from '@latitude-data/core'
+import { ProviderApiKeysRepository } from '@latitude-data/core/repositories'
import providerApiKeyPresenter from '$/presenters/providerApiKeyPresenter'
import { authProcedure } from '../procedures'
diff --git a/apps/web/src/actions/providerLogs/getProviderLogsForDocumentLogAction.ts b/apps/web/src/actions/providerLogs/getProviderLogsForDocumentLogAction.ts
index c56e5dddb..ac7ab44ee 100644
--- a/apps/web/src/actions/providerLogs/getProviderLogsForDocumentLogAction.ts
+++ b/apps/web/src/actions/providerLogs/getProviderLogsForDocumentLogAction.ts
@@ -1,6 +1,6 @@
'use server'
-import { ProviderLogsRepository } from '@latitude-data/core'
+import { ProviderLogsRepository } from '@latitude-data/core/repositories'
import { z } from 'zod'
import { authProcedure } from '../procedures'
diff --git a/apps/web/src/actions/sdk/runDocumentAction.ts b/apps/web/src/actions/sdk/runDocumentAction.ts
index a4434c3fa..47b0188e0 100644
--- a/apps/web/src/actions/sdk/runDocumentAction.ts
+++ b/apps/web/src/actions/sdk/runDocumentAction.ts
@@ -1,6 +1,6 @@
'use server'
-import { LogSources } from '@latitude-data/core'
+import { LogSources } from '@latitude-data/core/browser'
import {
type ChainEvent,
type StreamChainResponse,
diff --git a/apps/web/src/actions/user/loginAction.ts b/apps/web/src/actions/user/loginAction.ts
index 78808526b..82de69a19 100644
--- a/apps/web/src/actions/user/loginAction.ts
+++ b/apps/web/src/actions/user/loginAction.ts
@@ -1,7 +1,7 @@
'use server'
+import { createMagicLinkToken } from '@latitude-data/core/services/magicLinkTokens/create'
import { getUserFromCredentials } from '$/data-access'
-import { setSession } from '$/services/auth/setSession'
import { ROUTES } from '$/services/routes'
import { redirect } from 'next/navigation'
import { z } from 'zod'
@@ -11,14 +11,12 @@ export const loginAction = createServerAction()
.input(
z.object({
email: z.string().email(),
- password: z.string().min(3),
}),
{ type: 'formData' },
)
.handler(async ({ input }) => {
- const result = await getUserFromCredentials(input)
- const sessionData = result.unwrap()
+ const { user } = await getUserFromCredentials(input).then((r) => r.unwrap())
+ await createMagicLinkToken({ user }).then((r) => r.unwrap())
- setSession({ sessionData })
- redirect(ROUTES.root)
+ redirect(ROUTES.auth.magicLinkSent(user.email))
})
diff --git a/apps/web/src/actions/user/setupAction.ts b/apps/web/src/actions/user/setupAction.ts
index 1d8570d22..f69aa078d 100644
--- a/apps/web/src/actions/user/setupAction.ts
+++ b/apps/web/src/actions/user/setupAction.ts
@@ -1,6 +1,5 @@
'use server'
-import { isWorkspaceCreated } from '$/data-access'
import { setSession } from '$/services/auth/setSession'
import { ROUTES } from '$/services/routes'
import setupService from '$/services/user/setupService'
@@ -13,9 +12,6 @@ export const setupAction = createServerAction()
z.object({
name: z.string().min(1, { message: 'Name is a required field' }),
email: z.string().email(),
- password: z
- .string()
- .min(8, { message: 'Password must be at least 8 characters' }),
companyName: z
.string()
.min(1, { message: 'Workspace name is a required field' }),
@@ -23,14 +19,10 @@ export const setupAction = createServerAction()
{ type: 'formData' },
)
.handler(async ({ input }) => {
- const itWasAlreadySetup = await isWorkspaceCreated()
- if (itWasAlreadySetup) {
- throw new Error('Workspace already created')
- }
-
const result = await setupService(input)
const sessionData = result.unwrap()
setSession({ sessionData })
+
redirect(ROUTES.root)
})
diff --git a/apps/web/src/actions/users/fetch.test.ts b/apps/web/src/actions/users/fetch.test.ts
index a14609b99..e0a450a00 100644
--- a/apps/web/src/actions/users/fetch.test.ts
+++ b/apps/web/src/actions/users/fetch.test.ts
@@ -37,8 +37,6 @@ describe('getUsersAction', () => {
expect(error).toBeNull()
expect(data?.length).toEqual(1)
- // @ts-ignore - encryptedPassword is not defined on the User type for some reason?
- expect(data![0]!.encryptedPassword).toBeUndefined()
})
})
})
diff --git a/apps/web/src/actions/users/fetch.ts b/apps/web/src/actions/users/fetch.ts
index 3f6ab09e6..b152707e9 100644
--- a/apps/web/src/actions/users/fetch.ts
+++ b/apps/web/src/actions/users/fetch.ts
@@ -1,7 +1,6 @@
'use server'
-import { UsersRepository } from '@latitude-data/core'
-import userPresenter from '$/presenters/userPresenter'
+import { UsersRepository } from '@latitude-data/core/repositories'
import { authProcedure } from '../procedures'
@@ -10,8 +9,5 @@ export const getUsersActions = authProcedure
.handler(async ({ ctx }) => {
const usersScope = new UsersRepository(ctx.workspace.id)
- return usersScope
- .findAll()
- .then((r) => r.unwrap())
- .then((r) => r.map(userPresenter))
+ return usersScope.findAll().then((r) => r.unwrap())
})
diff --git a/apps/web/src/actions/users/invite.ts b/apps/web/src/actions/users/invite.ts
new file mode 100644
index 000000000..eb4191ff3
--- /dev/null
+++ b/apps/web/src/actions/users/invite.ts
@@ -0,0 +1,23 @@
+'use server'
+
+import { inviteUser } from '@latitude-data/core/services/users/invite'
+import { z } from 'zod'
+
+import { authProcedure } from '../procedures'
+
+export const inviteUserAction = authProcedure
+ .createServerAction()
+ .input(
+ z.object({
+ email: z.string(),
+ name: z.string(),
+ }),
+ )
+ .handler(async ({ input, ctx }) =>
+ inviteUser({
+ email: input.email,
+ name: input.name,
+ workspace: ctx.workspace,
+ author: ctx.user,
+ }).then((r) => r.unwrap()),
+ )
diff --git a/apps/web/src/actions/workspaces/update.ts b/apps/web/src/actions/workspaces/update.ts
index 9f992394a..a0e014e2b 100644
--- a/apps/web/src/actions/workspaces/update.ts
+++ b/apps/web/src/actions/workspaces/update.ts
@@ -1,6 +1,7 @@
'use server'
-import { updateWorkspace, WorkspacesRepository } from '@latitude-data/core'
+import { WorkspacesRepository } from '@latitude-data/core/repositories'
+import { updateWorkspace } from '@latitude-data/core/services/workspaces/update'
import { z } from 'zod'
import { authProcedure } from '../procedures'
diff --git a/apps/web/src/app/(private)/_data-access/index.ts b/apps/web/src/app/(private)/_data-access/index.ts
index 8a998ac99..07c00eb05 100644
--- a/apps/web/src/app/(private)/_data-access/index.ts
+++ b/apps/web/src/app/(private)/_data-access/index.ts
@@ -1,13 +1,13 @@
import { cache } from 'react'
+import { type Commit, type Project } from '@latitude-data/core/browser'
+import { NotFoundError } from '@latitude-data/core/lib/errors'
import {
CommitsRepository,
DocumentLogsRepository,
DocumentVersionsRepository,
- NotFoundError,
ProjectsRepository,
-} from '@latitude-data/core'
-import type { Commit, Project } from '@latitude-data/core/browser'
+} from '@latitude-data/core/repositories/index'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { notFound } from 'next/navigation'
diff --git a/apps/web/src/app/(private)/_lib/createSdk.ts b/apps/web/src/app/(private)/_lib/createSdk.ts
index 59b02230b..1098cce04 100644
--- a/apps/web/src/app/(private)/_lib/createSdk.ts
+++ b/apps/web/src/app/(private)/_lib/createSdk.ts
@@ -1,4 +1,5 @@
-import { LatitudeApiKeysRepository, Result } from '@latitude-data/core'
+import { Result } from '@latitude-data/core/lib/Result'
+import { LatitudeApiKeysRepository } from '@latitude-data/core/repositories'
import { LatitudeSdk } from '@latitude-data/sdk-js'
import env from '$/env'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
diff --git a/apps/web/src/app/(private)/page.tsx b/apps/web/src/app/(private)/page.tsx
index 1b69d000f..e0191f70b 100644
--- a/apps/web/src/app/(private)/page.tsx
+++ b/apps/web/src/app/(private)/page.tsx
@@ -1,5 +1,5 @@
-import { NotFoundError } from '@latitude-data/core'
import { type Project } from '@latitude-data/core/browser'
+import { NotFoundError } from '@latitude-data/core/lib/errors'
import { getFirstProjectCached } from '$/app/(private)/_data-access'
import { getCurrentUser, SessionData } from '$/services/auth/getCurrentUser'
import { ROUTES } from '$/services/routes'
diff --git a/apps/web/src/app/(private)/projects/[projectId]/page.tsx b/apps/web/src/app/(private)/projects/[projectId]/page.tsx
index 134833a3d..d5a897941 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/page.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/page.tsx
@@ -1,5 +1,5 @@
-import { HEAD_COMMIT, NotFoundError } from '@latitude-data/core'
-import type { Project } from '@latitude-data/core/browser'
+import { HEAD_COMMIT, type Project } from '@latitude-data/core/browser'
+import { NotFoundError } from '@latitude-data/core/lib/errors'
import {
findCommitCached,
findProjectCached,
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/PublishDraftCommitModal/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/PublishDraftCommitModal/index.tsx
index 3138a3e01..34933f1a0 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/PublishDraftCommitModal/index.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/PublishDraftCommitModal/index.tsx
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useState } from 'react'
-import { ChangedDocument } from '@latitude-data/core'
import { Commit, ModifiedDocumentType } from '@latitude-data/core/browser'
+import { ChangedDocument } from '@latitude-data/core/repositories'
import {
cn,
colors,
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/index.tsx
index 2bb56ca78..cc548f8ca 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/index.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/index.tsx
@@ -1,5 +1,10 @@
-import { CommitsRepository, CommitStatus } from '@latitude-data/core'
-import { Commit, DocumentVersion, Project } from '@latitude-data/core/browser'
+import {
+ Commit,
+ CommitStatus,
+ DocumentVersion,
+ Project,
+} from '@latitude-data/core/browser'
+import { CommitsRepository } from '@latitude-data/core/repositories/index'
import { DocumentSidebar } from '@latitude-data/web-ui'
import { fetchCommitsByProjectAction } from '$/actions/commits/fetchCommitsByProjectAction'
import { getDocumentsAtCommitCached } from '$/app/(private)/_data-access'
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/Metadata.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/Metadata.tsx
index 017049760..9b94e7328 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/Metadata.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/Metadata.tsx
@@ -1,7 +1,7 @@
import { ReactNode, useMemo } from 'react'
-import type { DocumentLogWithMetadata } from '@latitude-data/core'
import { ProviderLog } from '@latitude-data/core/browser'
+import { DocumentLogWithMetadata } from '@latitude-data/core/repositories'
import {
ClickToCopy,
Icons,
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/index.tsx
index 13c379d09..5e8380a5f 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/index.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/index.tsx
@@ -2,8 +2,8 @@
import { useState } from 'react'
-import type { DocumentLogWithMetadata } from '@latitude-data/core'
import { ProviderLog } from '@latitude-data/core/browser'
+import { DocumentLogWithMetadata } from '@latitude-data/core/repositories'
import { TabSelector } from '@latitude-data/web-ui'
import { DocumentLogMessages } from './Messages'
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx
index 16b3c4840..6b6a20ce1 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx
@@ -1,6 +1,6 @@
'use client'
-import { DocumentLogWithMetadata } from '@latitude-data/core'
+import { DocumentLogWithMetadata } from '@latitude-data/core/repositories'
import {
Badge,
cn,
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/index.tsx
index d15ee6066..02ad672da 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/index.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/index.tsx
@@ -2,7 +2,7 @@
import { useState } from 'react'
-import { DocumentLogWithMetadata } from '@latitude-data/core'
+import { DocumentLogWithMetadata } from '@latitude-data/core/repositories'
import useProviderLogs from '$/stores/providerLogs'
import { DocumentLogInfo } from './DocumentLogInfo'
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/layout.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/layout.tsx
index 85728a56e..aca1abe88 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/layout.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/layout.tsx
@@ -1,12 +1,15 @@
import { ReactNode } from 'react'
import {
- CommitsRepository,
HEAD_COMMIT,
- NotFoundError,
+ type Commit,
+ type Project,
+} from '@latitude-data/core/browser'
+import { NotFoundError } from '@latitude-data/core/lib/errors'
+import {
+ CommitsRepository,
ProjectsRepository,
-} from '@latitude-data/core'
-import type { Commit, Project } from '@latitude-data/core/browser'
+} from '@latitude-data/core/repositories/index'
import { CommitProvider, ProjectProvider } from '@latitude-data/web-ui'
import { BreadcrumpBadge } from '@latitude-data/web-ui/browser'
import { NAV_LINKS } from '$/app/(private)/_lib/constants'
diff --git a/apps/web/src/app/(private)/settings/_components/Memberships/New/index.tsx b/apps/web/src/app/(private)/settings/_components/Memberships/New/index.tsx
new file mode 100644
index 000000000..c569fe3ba
--- /dev/null
+++ b/apps/web/src/app/(private)/settings/_components/Memberships/New/index.tsx
@@ -0,0 +1,59 @@
+import {
+ Button,
+ CloseTrigger,
+ FormWrapper,
+ Input,
+ Modal,
+} from '@latitude-data/web-ui'
+import { useFormAction } from '$/hooks/useFormAction'
+import useUsers from '$/stores/users'
+
+export default function NewUser({
+ open,
+ setOpen,
+}: {
+ open: boolean
+ setOpen: (open: boolean) => void
+}) {
+ const { invite } = useUsers()
+ const { data, action } = useFormAction(invite, {
+ onSuccess: () => setOpen(false),
+ })
+ return (
+
+
+
+ >
+ }
+ >
+
+
+ )
+}
diff --git a/apps/web/src/app/(private)/settings/_components/Memberships/index.tsx b/apps/web/src/app/(private)/settings/_components/Memberships/index.tsx
new file mode 100644
index 000000000..7c30dee18
--- /dev/null
+++ b/apps/web/src/app/(private)/settings/_components/Memberships/index.tsx
@@ -0,0 +1,86 @@
+'use client'
+
+import { useState } from 'react'
+
+import type { SafeUser } from '@latitude-data/core/browser'
+import {
+ Button,
+ Icons,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+ Text,
+} from '@latitude-data/web-ui'
+import useUsers from '$/stores/users'
+
+import NewUser from './New'
+
+export default function Memberships() {
+ const [open, setOpen] = useState(false)
+ const { data: users, destroy } = useUsers()
+
+ return (
+
+
+
+ Workspace Users
+
+
+
+ {users.length > 0 && }
+
+
+ )
+}
+
+function UsersTable({
+ users,
+ destroy,
+}: {
+ users: SafeUser[]
+ destroy: Function
+}) {
+ return (
+
+
+
+ Name
+ Email
+ Confirmed At
+
+
+
+
+ {users.map((user) => (
+
+
+ {user.name}
+
+
+ {user.email}
+
+
+
+ {user.confirmedAt?.toISOString() || '-'}
+
+
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/web/src/app/(private)/settings/page.tsx b/apps/web/src/app/(private)/settings/page.tsx
index a5c72c892..434b11e62 100644
--- a/apps/web/src/app/(private)/settings/page.tsx
+++ b/apps/web/src/app/(private)/settings/page.tsx
@@ -1,3 +1,4 @@
+import Memberships from './_components/Memberships'
import ProviderApiKeys from './_components/ProviderApiKeys'
import WorkspaceName from './_components/WorkspaceName'
@@ -7,6 +8,7 @@ export default function SettingsPage() {
)
diff --git a/apps/web/src/app/(public)/_components/Footer/index.tsx b/apps/web/src/app/(public)/_components/Footer/index.tsx
index 2320f8cef..2b1ef6609 100644
--- a/apps/web/src/app/(public)/_components/Footer/index.tsx
+++ b/apps/web/src/app/(public)/_components/Footer/index.tsx
@@ -2,7 +2,7 @@ import {
LATITUDE_DOCS_URL,
LATITUDE_EMAIL,
LATITUDE_SLACK_URL,
-} from '@latitude-data/core'
+} from '@latitude-data/core/browser'
import { Text } from '@latitude-data/web-ui'
export default function AuthFooter() {
diff --git a/apps/web/src/app/(public)/_data_access/index.ts b/apps/web/src/app/(public)/_data_access/index.ts
new file mode 100644
index 000000000..17b91e435
--- /dev/null
+++ b/apps/web/src/app/(public)/_data_access/index.ts
@@ -0,0 +1,19 @@
+import { cache } from 'react'
+
+import {
+ unsafelyFindMembershipByToken,
+ unsafelyFindWorkspace,
+ unsafelyGetUser,
+} from '@latitude-data/core/data-access'
+
+export const findMembershipByTokenCache = cache(async (token: string) => {
+ return await unsafelyFindMembershipByToken(token).then((r) => r.unwrap())
+})
+
+export const findWorkspaceCache = cache(async (id: number) => {
+ return await unsafelyFindWorkspace(id).then((r) => r.unwrap())
+})
+
+export const findUserCache = cache(async (id: string) => {
+ return await unsafelyGetUser(id)
+})
diff --git a/apps/web/src/app/(public)/invitations/[token]/InvitationForm/index.tsx b/apps/web/src/app/(public)/invitations/[token]/InvitationForm/index.tsx
new file mode 100644
index 000000000..ed835c4fa
--- /dev/null
+++ b/apps/web/src/app/(public)/invitations/[token]/InvitationForm/index.tsx
@@ -0,0 +1,65 @@
+'use client'
+
+import { ReactNode } from 'react'
+
+import { Membership, SafeUser } from '@latitude-data/core/browser'
+import { Button, FormWrapper, Input, useToast } from '@latitude-data/web-ui'
+import { acceptInvitationAction } from '$/actions/invitations/accept'
+import { useServerAction } from 'zsa-react'
+
+export default function InvitationForm({
+ user,
+ membership,
+ footer,
+}: {
+ user: SafeUser
+ membership: Membership
+ footer: ReactNode
+}) {
+ const { toast } = useToast()
+ const { isPending, error, executeFormAction } = useServerAction(
+ acceptInvitationAction,
+ {
+ onError: ({ err }) => {
+ if (err.code === 'ERROR') {
+ toast({
+ title: 'Saving failed',
+ description: err.message,
+ variant: 'destructive',
+ })
+ }
+ },
+ },
+ )
+ const errors = error?.fieldErrors
+
+ return (
+
+ )
+}
diff --git a/apps/web/src/app/(public)/invitations/[token]/page.tsx b/apps/web/src/app/(public)/invitations/[token]/page.tsx
new file mode 100644
index 000000000..2c589ea2f
--- /dev/null
+++ b/apps/web/src/app/(public)/invitations/[token]/page.tsx
@@ -0,0 +1,47 @@
+import { Card, CardContent, FocusHeader } from '@latitude-data/web-ui'
+import { FocusLayout } from '$/components/layouts'
+import { ROUTES } from '$/services/routes'
+import { redirect } from 'next/navigation'
+
+import AuthFooter from '../../_components/Footer'
+import {
+ findMembershipByTokenCache,
+ findUserCache,
+ findWorkspaceCache,
+} from '../../_data_access'
+import InvitationForm from './InvitationForm'
+
+export const dynamic = 'force-dynamic'
+
+export default async function InvitationPage({
+ params,
+}: {
+ params: { token: string }
+}) {
+ const { token } = params
+ const m = await findMembershipByTokenCache(token as string)
+ if (!m) return redirect(ROUTES.root)
+
+ const workspace = await findWorkspaceCache(m.workspaceId)
+ if (!workspace) return redirect(ROUTES.root)
+
+ const user = await findUserCache(m.userId)
+ if (!user) return redirect(ROUTES.root)
+
+ return (
+
+ }
+ >
+
+
+ } />
+
+
+
+ )
+}
diff --git a/apps/web/src/app/(public)/login/LoginForm/index.tsx b/apps/web/src/app/(public)/login/LoginForm/index.tsx
index 354dd0ccf..ef9065175 100644
--- a/apps/web/src/app/(public)/login/LoginForm/index.tsx
+++ b/apps/web/src/app/(public)/login/LoginForm/index.tsx
@@ -12,7 +12,7 @@ export default function LoginForm({ footer }: { footer: ReactNode }) {
onError: ({ err }) => {
if (err.code === 'ERROR') {
toast({
- title: 'Saving failed',
+ title: 'Error',
description: err.message,
variant: 'destructive',
})
@@ -31,14 +31,6 @@ export default function LoginForm({ footer }: { footer: ReactNode }) {
placeholder='Ex.: jon@example.com'
errors={errors?.email}
/>
-
diff --git a/apps/web/src/app/(public)/magic-links/confirm/[token]/ConfirmMagicLink.tsx b/apps/web/src/app/(public)/magic-links/confirm/[token]/ConfirmMagicLink.tsx
new file mode 100644
index 000000000..6f9ebdf75
--- /dev/null
+++ b/apps/web/src/app/(public)/magic-links/confirm/[token]/ConfirmMagicLink.tsx
@@ -0,0 +1,30 @@
+import { confirmMagicLinkToken } from '@latitude-data/core/services/magicLinkTokens/confirm'
+import { FocusHeader } from '@latitude-data/web-ui'
+import { FocusLayout } from '$/components/layouts'
+import { ROUTES } from '$/services/routes'
+import { redirect } from 'next/navigation'
+
+export default async function ConfirmMagicLink({
+ params,
+}: {
+ params: { token: string }
+}) {
+ const { token } = params
+
+ await confirmMagicLinkToken(token).then((r) => r.unwrap())
+
+ setTimeout(() => {
+ redirect(ROUTES.root)
+ }, 5000)
+
+ return (
+
+ }
+ />
+ )
+}
diff --git a/apps/web/src/app/(public)/magic-links/confirm/[token]/page.tsx b/apps/web/src/app/(public)/magic-links/confirm/[token]/page.tsx
new file mode 100644
index 000000000..57e8c0631
--- /dev/null
+++ b/apps/web/src/app/(public)/magic-links/confirm/[token]/page.tsx
@@ -0,0 +1,34 @@
+'use client'
+
+import { useEffect } from 'react'
+
+import { FocusHeader } from '@latitude-data/web-ui'
+import { confirmMagicLinkTokenAction } from '$/actions/magicLinkTokens/confirm'
+import { FocusLayout } from '$/components/layouts'
+import useLatitudeAction from '$/hooks/useLatitudeAction'
+
+export default function ConfirmMagicLink({
+ params,
+}: {
+ params: { token: string }
+}) {
+ const { token } = params
+ const { execute } = useLatitudeAction(confirmMagicLinkTokenAction, {
+ onSuccess: () => {}, // We don't want the default toast message in this case
+ })
+
+ useEffect(() => {
+ setTimeout(() => execute({ token }), 1000)
+ }, [execute])
+
+ return (
+
+ }
+ />
+ )
+}
diff --git a/apps/web/src/app/(public)/magic-links/sent/page.tsx b/apps/web/src/app/(public)/magic-links/sent/page.tsx
new file mode 100644
index 000000000..2263bdd6d
--- /dev/null
+++ b/apps/web/src/app/(public)/magic-links/sent/page.tsx
@@ -0,0 +1,24 @@
+import { FocusHeader } from '@latitude-data/web-ui'
+import { FocusLayout } from '$/components/layouts'
+import { ROUTES } from '$/services/routes'
+import { redirect } from 'next/navigation'
+
+export default function MagicLinkSent({
+ searchParams,
+}: {
+ searchParams: { email?: string }
+}) {
+ const { email } = searchParams
+ if (!email) return redirect(ROUTES.root)
+
+ return (
+
+ }
+ />
+ )
+}
diff --git a/apps/web/src/app/(public)/setup/SetupForm/index.tsx b/apps/web/src/app/(public)/setup/SetupForm/index.tsx
index 26c1e25ba..f2b87f221 100644
--- a/apps/web/src/app/(public)/setup/SetupForm/index.tsx
+++ b/apps/web/src/app/(public)/setup/SetupForm/index.tsx
@@ -28,28 +28,20 @@ export default function SetupForm({ footer }: { footer: ReactNode }) {
name='name'
autoComplete='name'
label='Name'
- placeholder='Your name'
+ placeholder='Jon Snow'
errors={errors?.name}
/>
-