Restore Ultracite + fix sidebar (#1233)
This commit is contained in:
parent
8fbfc253fa
commit
947ed094a6
177 changed files with 6908 additions and 8306 deletions
|
|
@ -1,10 +1,10 @@
|
|||
import type { UserType } from '@/app/(auth)/auth';
|
||||
import type { ChatModel } from './models';
|
||||
import type { UserType } from "@/app/(auth)/auth";
|
||||
import type { ChatModel } from "./models";
|
||||
|
||||
interface Entitlements {
|
||||
type Entitlements = {
|
||||
maxMessagesPerDay: number;
|
||||
availableChatModelIds: Array<ChatModel['id']>;
|
||||
}
|
||||
availableChatModelIds: ChatModel["id"][];
|
||||
};
|
||||
|
||||
export const entitlementsByUserType: Record<UserType, Entitlements> = {
|
||||
/*
|
||||
|
|
@ -12,7 +12,7 @@ export const entitlementsByUserType: Record<UserType, Entitlements> = {
|
|||
*/
|
||||
guest: {
|
||||
maxMessagesPerDay: 20,
|
||||
availableChatModelIds: ['chat-model', 'chat-model-reasoning'],
|
||||
availableChatModelIds: ["chat-model", "chat-model-reasoning"],
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
@ -20,7 +20,7 @@ export const entitlementsByUserType: Record<UserType, Entitlements> = {
|
|||
*/
|
||||
regular: {
|
||||
maxMessagesPerDay: 100,
|
||||
availableChatModelIds: ['chat-model', 'chat-model-reasoning'],
|
||||
availableChatModelIds: ["chat-model", "chat-model-reasoning"],
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
import type { LanguageModel } from 'ai';
|
||||
import type { LanguageModel } from "ai";
|
||||
|
||||
const createMockModel = (): LanguageModel => {
|
||||
return {
|
||||
specificationVersion: 'v2',
|
||||
provider: 'mock',
|
||||
modelId: 'mock-model',
|
||||
defaultObjectGenerationMode: 'tool',
|
||||
specificationVersion: "v2",
|
||||
provider: "mock",
|
||||
modelId: "mock-model",
|
||||
defaultObjectGenerationMode: "tool",
|
||||
supportedUrls: [],
|
||||
supportsImageUrls: false,
|
||||
supportsStructuredOutputs: false,
|
||||
doGenerate: async () => ({
|
||||
rawCall: { rawPrompt: null, rawSettings: {} },
|
||||
finishReason: 'stop',
|
||||
finishReason: "stop",
|
||||
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
||||
content: [{ type: 'text', text: 'Hello, world!' }],
|
||||
content: [{ type: "text", text: "Hello, world!" }],
|
||||
warnings: [],
|
||||
}),
|
||||
doStream: async () => ({
|
||||
stream: new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue({
|
||||
type: 'text-delta',
|
||||
id: 'mock-id',
|
||||
delta: 'Mock response',
|
||||
type: "text-delta",
|
||||
id: "mock-id",
|
||||
delta: "Mock response",
|
||||
});
|
||||
controller.close();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { simulateReadableStream } from 'ai';
|
||||
import { MockLanguageModelV2 } from 'ai/test';
|
||||
import { getResponseChunksByPrompt } from '@/tests/prompts/utils';
|
||||
import { simulateReadableStream } from "ai";
|
||||
import { MockLanguageModelV2 } from "ai/test";
|
||||
import { getResponseChunksByPrompt } from "@/tests/prompts/utils";
|
||||
|
||||
export const chatModel = new MockLanguageModelV2({
|
||||
doGenerate: async () => ({
|
||||
rawCall: { rawPrompt: null, rawSettings: {} },
|
||||
finishReason: 'stop',
|
||||
finishReason: "stop",
|
||||
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
||||
content: [{ type: 'text', text: 'Hello, world!' }],
|
||||
content: [{ type: "text", text: "Hello, world!" }],
|
||||
warnings: [],
|
||||
}),
|
||||
doStream: async ({ prompt }) => ({
|
||||
|
|
@ -23,9 +23,9 @@ export const chatModel = new MockLanguageModelV2({
|
|||
export const reasoningModel = new MockLanguageModelV2({
|
||||
doGenerate: async () => ({
|
||||
rawCall: { rawPrompt: null, rawSettings: {} },
|
||||
finishReason: 'stop',
|
||||
finishReason: "stop",
|
||||
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
||||
content: [{ type: 'text', text: 'Hello, world!' }],
|
||||
content: [{ type: "text", text: "Hello, world!" }],
|
||||
warnings: [],
|
||||
}),
|
||||
doStream: async ({ prompt }) => ({
|
||||
|
|
@ -41,9 +41,9 @@ export const reasoningModel = new MockLanguageModelV2({
|
|||
export const titleModel = new MockLanguageModelV2({
|
||||
doGenerate: async () => ({
|
||||
rawCall: { rawPrompt: null, rawSettings: {} },
|
||||
finishReason: 'stop',
|
||||
finishReason: "stop",
|
||||
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
||||
content: [{ type: 'text', text: 'This is a test title' }],
|
||||
content: [{ type: "text", text: "This is a test title" }],
|
||||
warnings: [],
|
||||
}),
|
||||
doStream: async () => ({
|
||||
|
|
@ -51,12 +51,12 @@ export const titleModel = new MockLanguageModelV2({
|
|||
chunkDelayInMs: 500,
|
||||
initialDelayInMs: 1000,
|
||||
chunks: [
|
||||
{ id: '1', type: 'text-start' },
|
||||
{ id: '1', type: 'text-delta', delta: 'This is a test title' },
|
||||
{ id: '1', type: 'text-end' },
|
||||
{ id: "1", type: "text-start" },
|
||||
{ id: "1", type: "text-delta", delta: "This is a test title" },
|
||||
{ id: "1", type: "text-end" },
|
||||
{
|
||||
type: 'finish',
|
||||
finishReason: 'stop',
|
||||
type: "finish",
|
||||
finishReason: "stop",
|
||||
usage: { inputTokens: 3, outputTokens: 10, totalTokens: 13 },
|
||||
},
|
||||
],
|
||||
|
|
@ -68,9 +68,9 @@ export const titleModel = new MockLanguageModelV2({
|
|||
export const artifactModel = new MockLanguageModelV2({
|
||||
doGenerate: async () => ({
|
||||
rawCall: { rawPrompt: null, rawSettings: {} },
|
||||
finishReason: 'stop',
|
||||
finishReason: "stop",
|
||||
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
||||
content: [{ type: 'text', text: 'Hello, world!' }],
|
||||
content: [{ type: "text", text: "Hello, world!" }],
|
||||
warnings: [],
|
||||
}),
|
||||
doStream: async ({ prompt }) => ({
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
export const DEFAULT_CHAT_MODEL: string = 'chat-model';
|
||||
export const DEFAULT_CHAT_MODEL: string = "chat-model";
|
||||
|
||||
export interface ChatModel {
|
||||
export type ChatModel = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
};
|
||||
|
||||
export const chatModels: Array<ChatModel> = [
|
||||
export const chatModels: ChatModel[] = [
|
||||
{
|
||||
id: 'chat-model',
|
||||
name: 'Grok Vision',
|
||||
description: 'Advanced multimodal model with vision and text capabilities',
|
||||
id: "chat-model",
|
||||
name: "Grok Vision",
|
||||
description: "Advanced multimodal model with vision and text capabilities",
|
||||
},
|
||||
{
|
||||
id: 'chat-model-reasoning',
|
||||
name: 'Grok Reasoning',
|
||||
id: "chat-model-reasoning",
|
||||
name: "Grok Reasoning",
|
||||
description:
|
||||
'Uses advanced chain-of-thought reasoning for complex problems',
|
||||
"Uses advanced chain-of-thought reasoning for complex problems",
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { ArtifactKind } from '@/components/artifact';
|
||||
import type { Geo } from '@vercel/functions';
|
||||
import type { Geo } from "@vercel/functions";
|
||||
import type { ArtifactKind } from "@/components/artifact";
|
||||
|
||||
export const artifactsPrompt = `
|
||||
Artifacts is a special user interface mode that helps users with writing, editing, and other content creation tasks. When artifact is open, it is on the right side of the screen, while the conversation is on the left side. When creating or updating documents, changes are reflected in real-time on the artifacts and visible to the user.
|
||||
|
|
@ -33,14 +33,14 @@ Do not update document right after creating it. Wait for user feedback or reques
|
|||
`;
|
||||
|
||||
export const regularPrompt =
|
||||
'You are a friendly assistant! Keep your responses concise and helpful.';
|
||||
"You are a friendly assistant! Keep your responses concise and helpful.";
|
||||
|
||||
export interface RequestHints {
|
||||
latitude: Geo['latitude'];
|
||||
longitude: Geo['longitude'];
|
||||
city: Geo['city'];
|
||||
country: Geo['country'];
|
||||
}
|
||||
export type RequestHints = {
|
||||
latitude: Geo["latitude"];
|
||||
longitude: Geo["longitude"];
|
||||
city: Geo["city"];
|
||||
country: Geo["country"];
|
||||
};
|
||||
|
||||
export const getRequestPromptFromHints = (requestHints: RequestHints) => `\
|
||||
About the origin of user's request:
|
||||
|
|
@ -59,11 +59,11 @@ export const systemPrompt = ({
|
|||
}) => {
|
||||
const requestPrompt = getRequestPromptFromHints(requestHints);
|
||||
|
||||
if (selectedChatModel === 'chat-model-reasoning') {
|
||||
if (selectedChatModel === "chat-model-reasoning") {
|
||||
return `${regularPrompt}\n\n${requestPrompt}`;
|
||||
} else {
|
||||
return `${regularPrompt}\n\n${requestPrompt}\n\n${artifactsPrompt}`;
|
||||
}
|
||||
|
||||
return `${regularPrompt}\n\n${requestPrompt}\n\n${artifactsPrompt}`;
|
||||
};
|
||||
|
||||
export const codePrompt = `
|
||||
|
|
@ -98,24 +98,17 @@ You are a spreadsheet creation assistant. Create a spreadsheet in csv format bas
|
|||
|
||||
export const updateDocumentPrompt = (
|
||||
currentContent: string | null,
|
||||
type: ArtifactKind,
|
||||
) =>
|
||||
type === 'text'
|
||||
? `\
|
||||
Improve the following contents of the document based on the given prompt.
|
||||
type: ArtifactKind
|
||||
) => {
|
||||
let mediaType = "document";
|
||||
|
||||
${currentContent}
|
||||
`
|
||||
: type === 'code'
|
||||
? `\
|
||||
Improve the following code snippet based on the given prompt.
|
||||
if (type === "code") {
|
||||
mediaType = "code snippet";
|
||||
} else if (type === "sheet") {
|
||||
mediaType = "spreadsheet";
|
||||
}
|
||||
|
||||
${currentContent}
|
||||
`
|
||||
: type === 'sheet'
|
||||
? `\
|
||||
Improve the following spreadsheet based on the given prompt.
|
||||
return `Improve the following contents of the ${mediaType} based on the given prompt.
|
||||
|
||||
${currentContent}
|
||||
`
|
||||
: '';
|
||||
${currentContent}`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { gateway } from "@ai-sdk/gateway";
|
||||
import {
|
||||
customProvider,
|
||||
extractReasoningMiddleware,
|
||||
wrapLanguageModel,
|
||||
} from 'ai';
|
||||
import { gateway } from '@ai-sdk/gateway';
|
||||
import { isTestEnvironment } from '../constants';
|
||||
} from "ai";
|
||||
import { isTestEnvironment } from "../constants";
|
||||
|
||||
export const myProvider = isTestEnvironment
|
||||
? (() => {
|
||||
|
|
@ -13,24 +13,24 @@ export const myProvider = isTestEnvironment
|
|||
chatModel,
|
||||
reasoningModel,
|
||||
titleModel,
|
||||
} = require('./models.mock');
|
||||
} = require("./models.mock");
|
||||
return customProvider({
|
||||
languageModels: {
|
||||
'chat-model': chatModel,
|
||||
'chat-model-reasoning': reasoningModel,
|
||||
'title-model': titleModel,
|
||||
'artifact-model': artifactModel,
|
||||
"chat-model": chatModel,
|
||||
"chat-model-reasoning": reasoningModel,
|
||||
"title-model": titleModel,
|
||||
"artifact-model": artifactModel,
|
||||
},
|
||||
});
|
||||
})()
|
||||
: customProvider({
|
||||
languageModels: {
|
||||
'chat-model': gateway.languageModel('xai/grok-2-vision-1212'),
|
||||
'chat-model-reasoning': wrapLanguageModel({
|
||||
model: gateway.languageModel('xai/grok-3-mini'),
|
||||
middleware: extractReasoningMiddleware({ tagName: 'think' }),
|
||||
"chat-model": gateway.languageModel("xai/grok-2-vision-1212"),
|
||||
"chat-model-reasoning": wrapLanguageModel({
|
||||
model: gateway.languageModel("xai/grok-3-mini"),
|
||||
middleware: extractReasoningMiddleware({ tagName: "think" }),
|
||||
}),
|
||||
'title-model': gateway.languageModel('xai/grok-2-1212'),
|
||||
'artifact-model': gateway.languageModel('xai/grok-2-1212'),
|
||||
"title-model": gateway.languageModel("xai/grok-2-1212"),
|
||||
"artifact-model": gateway.languageModel("xai/grok-2-1212"),
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
import { generateUUID } from '@/lib/utils';
|
||||
import { tool, type UIMessageStreamWriter } from 'ai';
|
||||
import { z } from 'zod';
|
||||
import type { Session } from 'next-auth';
|
||||
import { tool, type UIMessageStreamWriter } from "ai";
|
||||
import type { Session } from "next-auth";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
artifactKinds,
|
||||
documentHandlersByArtifactKind,
|
||||
} from '@/lib/artifacts/server';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
} from "@/lib/artifacts/server";
|
||||
import type { ChatMessage } from "@/lib/types";
|
||||
import { generateUUID } from "@/lib/utils";
|
||||
|
||||
interface CreateDocumentProps {
|
||||
type CreateDocumentProps = {
|
||||
session: Session;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
}
|
||||
};
|
||||
|
||||
export const createDocument = ({ session, dataStream }: CreateDocumentProps) =>
|
||||
tool({
|
||||
description:
|
||||
'Create a document for a writing or content creation activities. This tool will call other functions that will generate the contents of the document based on the title and kind.',
|
||||
"Create a document for a writing or content creation activities. This tool will call other functions that will generate the contents of the document based on the title and kind.",
|
||||
inputSchema: z.object({
|
||||
title: z.string(),
|
||||
kind: z.enum(artifactKinds),
|
||||
|
|
@ -25,32 +25,32 @@ export const createDocument = ({ session, dataStream }: CreateDocumentProps) =>
|
|||
const id = generateUUID();
|
||||
|
||||
dataStream.write({
|
||||
type: 'data-kind',
|
||||
type: "data-kind",
|
||||
data: kind,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
dataStream.write({
|
||||
type: 'data-id',
|
||||
type: "data-id",
|
||||
data: id,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
dataStream.write({
|
||||
type: 'data-title',
|
||||
type: "data-title",
|
||||
data: title,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
dataStream.write({
|
||||
type: 'data-clear',
|
||||
type: "data-clear",
|
||||
data: null,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
const documentHandler = documentHandlersByArtifactKind.find(
|
||||
(documentHandlerByArtifactKind) =>
|
||||
documentHandlerByArtifactKind.kind === kind,
|
||||
documentHandlerByArtifactKind.kind === kind
|
||||
);
|
||||
|
||||
if (!documentHandler) {
|
||||
|
|
@ -64,13 +64,13 @@ export const createDocument = ({ session, dataStream }: CreateDocumentProps) =>
|
|||
session,
|
||||
});
|
||||
|
||||
dataStream.write({ type: 'data-finish', data: null, transient: true });
|
||||
dataStream.write({ type: "data-finish", data: null, transient: true });
|
||||
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
kind,
|
||||
content: 'A document was created and is now visible to the user.',
|
||||
content: "A document was created and is now visible to the user.",
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { tool } from 'ai';
|
||||
import { z } from 'zod';
|
||||
import { tool } from "ai";
|
||||
import { z } from "zod";
|
||||
|
||||
export const getWeather = tool({
|
||||
description: 'Get the current weather at a location',
|
||||
description: "Get the current weather at a location",
|
||||
inputSchema: z.object({
|
||||
latitude: z.number(),
|
||||
longitude: z.number(),
|
||||
}),
|
||||
execute: async ({ latitude, longitude }) => {
|
||||
const response = await fetch(
|
||||
`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m&hourly=temperature_2m&daily=sunrise,sunset&timezone=auto`,
|
||||
`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m&hourly=temperature_2m&daily=sunrise,sunset&timezone=auto`
|
||||
);
|
||||
|
||||
const weatherData = await response.json();
|
||||
|
|
|
|||
|
|
@ -1,67 +1,68 @@
|
|||
import { z } from 'zod';
|
||||
import type { Session } from 'next-auth';
|
||||
import { streamObject, tool, type UIMessageStreamWriter } from 'ai';
|
||||
import { getDocumentById, saveSuggestions } from '@/lib/db/queries';
|
||||
import type { Suggestion } from '@/lib/db/schema';
|
||||
import { generateUUID } from '@/lib/utils';
|
||||
import { myProvider } from '../providers';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
import { streamObject, tool, type UIMessageStreamWriter } from "ai";
|
||||
import type { Session } from "next-auth";
|
||||
import { z } from "zod";
|
||||
import { getDocumentById, saveSuggestions } from "@/lib/db/queries";
|
||||
import type { Suggestion } from "@/lib/db/schema";
|
||||
import type { ChatMessage } from "@/lib/types";
|
||||
import { generateUUID } from "@/lib/utils";
|
||||
import { myProvider } from "../providers";
|
||||
|
||||
interface RequestSuggestionsProps {
|
||||
type RequestSuggestionsProps = {
|
||||
session: Session;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
}
|
||||
};
|
||||
|
||||
export const requestSuggestions = ({
|
||||
session,
|
||||
dataStream,
|
||||
}: RequestSuggestionsProps) =>
|
||||
tool({
|
||||
description: 'Request suggestions for a document',
|
||||
description: "Request suggestions for a document",
|
||||
inputSchema: z.object({
|
||||
documentId: z
|
||||
.string()
|
||||
.describe('The ID of the document to request edits'),
|
||||
.describe("The ID of the document to request edits"),
|
||||
}),
|
||||
execute: async ({ documentId }) => {
|
||||
const document = await getDocumentById({ id: documentId });
|
||||
|
||||
if (!document || !document.content) {
|
||||
return {
|
||||
error: 'Document not found',
|
||||
error: "Document not found",
|
||||
};
|
||||
}
|
||||
|
||||
const suggestions: Array<
|
||||
Omit<Suggestion, 'userId' | 'createdAt' | 'documentCreatedAt'>
|
||||
> = [];
|
||||
const suggestions: Omit<
|
||||
Suggestion,
|
||||
"userId" | "createdAt" | "documentCreatedAt"
|
||||
>[] = [];
|
||||
|
||||
const { elementStream } = streamObject({
|
||||
model: myProvider.languageModel('artifact-model'),
|
||||
model: myProvider.languageModel("artifact-model"),
|
||||
system:
|
||||
'You are a help writing assistant. Given a piece of writing, please offer suggestions to improve the piece of writing and describe the change. It is very important for the edits to contain full sentences instead of just words. Max 5 suggestions.',
|
||||
"You are a help writing assistant. Given a piece of writing, please offer suggestions to improve the piece of writing and describe the change. It is very important for the edits to contain full sentences instead of just words. Max 5 suggestions.",
|
||||
prompt: document.content,
|
||||
output: 'array',
|
||||
output: "array",
|
||||
schema: z.object({
|
||||
originalSentence: z.string().describe('The original sentence'),
|
||||
suggestedSentence: z.string().describe('The suggested sentence'),
|
||||
description: z.string().describe('The description of the suggestion'),
|
||||
originalSentence: z.string().describe("The original sentence"),
|
||||
suggestedSentence: z.string().describe("The suggested sentence"),
|
||||
description: z.string().describe("The description of the suggestion"),
|
||||
}),
|
||||
});
|
||||
|
||||
for await (const element of elementStream) {
|
||||
// @ts-ignore todo: fix type
|
||||
// @ts-expect-error todo: fix type
|
||||
const suggestion: Suggestion = {
|
||||
originalText: element.originalSentence,
|
||||
suggestedText: element.suggestedSentence,
|
||||
description: element.description,
|
||||
id: generateUUID(),
|
||||
documentId: documentId,
|
||||
documentId,
|
||||
isResolved: false,
|
||||
};
|
||||
|
||||
dataStream.write({
|
||||
type: 'data-suggestion',
|
||||
type: "data-suggestion",
|
||||
data: suggestion,
|
||||
transient: true,
|
||||
});
|
||||
|
|
@ -86,7 +87,7 @@ export const requestSuggestions = ({
|
|||
id: documentId,
|
||||
title: document.title,
|
||||
kind: document.kind,
|
||||
message: 'Suggestions have been added to the document',
|
||||
message: "Suggestions have been added to the document",
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,42 +1,42 @@
|
|||
import { tool, type UIMessageStreamWriter } from 'ai';
|
||||
import type { Session } from 'next-auth';
|
||||
import { z } from 'zod';
|
||||
import { getDocumentById } from '@/lib/db/queries';
|
||||
import { documentHandlersByArtifactKind } from '@/lib/artifacts/server';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
import { tool, type UIMessageStreamWriter } from "ai";
|
||||
import type { Session } from "next-auth";
|
||||
import { z } from "zod";
|
||||
import { documentHandlersByArtifactKind } from "@/lib/artifacts/server";
|
||||
import { getDocumentById } from "@/lib/db/queries";
|
||||
import type { ChatMessage } from "@/lib/types";
|
||||
|
||||
interface UpdateDocumentProps {
|
||||
type UpdateDocumentProps = {
|
||||
session: Session;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateDocument = ({ session, dataStream }: UpdateDocumentProps) =>
|
||||
tool({
|
||||
description: 'Update a document with the given description.',
|
||||
description: "Update a document with the given description.",
|
||||
inputSchema: z.object({
|
||||
id: z.string().describe('The ID of the document to update'),
|
||||
id: z.string().describe("The ID of the document to update"),
|
||||
description: z
|
||||
.string()
|
||||
.describe('The description of changes that need to be made'),
|
||||
.describe("The description of changes that need to be made"),
|
||||
}),
|
||||
execute: async ({ id, description }) => {
|
||||
const document = await getDocumentById({ id });
|
||||
|
||||
if (!document) {
|
||||
return {
|
||||
error: 'Document not found',
|
||||
error: "Document not found",
|
||||
};
|
||||
}
|
||||
|
||||
dataStream.write({
|
||||
type: 'data-clear',
|
||||
type: "data-clear",
|
||||
data: null,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
const documentHandler = documentHandlersByArtifactKind.find(
|
||||
(documentHandlerByArtifactKind) =>
|
||||
documentHandlerByArtifactKind.kind === document.kind,
|
||||
documentHandlerByArtifactKind.kind === document.kind
|
||||
);
|
||||
|
||||
if (!documentHandler) {
|
||||
|
|
@ -50,13 +50,13 @@ export const updateDocument = ({ session, dataStream }: UpdateDocumentProps) =>
|
|||
session,
|
||||
});
|
||||
|
||||
dataStream.write({ type: 'data-finish', data: null, transient: true });
|
||||
dataStream.write({ type: "data-finish", data: null, transient: true });
|
||||
|
||||
return {
|
||||
id,
|
||||
title: document.title,
|
||||
kind: document.kind,
|
||||
content: 'The document has been updated successfully.',
|
||||
content: "The document has been updated successfully.",
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,40 +1,40 @@
|
|||
import { codeDocumentHandler } from '@/artifacts/code/server';
|
||||
import { sheetDocumentHandler } from '@/artifacts/sheet/server';
|
||||
import { textDocumentHandler } from '@/artifacts/text/server';
|
||||
import type { ArtifactKind } from '@/components/artifact';
|
||||
import type { Document } from '../db/schema';
|
||||
import { saveDocument } from '../db/queries';
|
||||
import type { Session } from 'next-auth';
|
||||
import type { UIMessageStreamWriter } from 'ai';
|
||||
import type { ChatMessage } from '../types';
|
||||
import type { UIMessageStreamWriter } from "ai";
|
||||
import type { Session } from "next-auth";
|
||||
import { codeDocumentHandler } from "@/artifacts/code/server";
|
||||
import { sheetDocumentHandler } from "@/artifacts/sheet/server";
|
||||
import { textDocumentHandler } from "@/artifacts/text/server";
|
||||
import type { ArtifactKind } from "@/components/artifact";
|
||||
import { saveDocument } from "../db/queries";
|
||||
import type { Document } from "../db/schema";
|
||||
import type { ChatMessage } from "../types";
|
||||
|
||||
export interface SaveDocumentProps {
|
||||
export type SaveDocumentProps = {
|
||||
id: string;
|
||||
title: string;
|
||||
kind: ArtifactKind;
|
||||
content: string;
|
||||
userId: string;
|
||||
}
|
||||
};
|
||||
|
||||
export interface CreateDocumentCallbackProps {
|
||||
export type CreateDocumentCallbackProps = {
|
||||
id: string;
|
||||
title: string;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
session: Session;
|
||||
}
|
||||
};
|
||||
|
||||
export interface UpdateDocumentCallbackProps {
|
||||
export type UpdateDocumentCallbackProps = {
|
||||
document: Document;
|
||||
description: string;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
session: Session;
|
||||
}
|
||||
};
|
||||
|
||||
export interface DocumentHandler<T = ArtifactKind> {
|
||||
export type DocumentHandler<T = ArtifactKind> = {
|
||||
kind: T;
|
||||
onCreateDocument: (args: CreateDocumentCallbackProps) => Promise<void>;
|
||||
onUpdateDocument: (args: UpdateDocumentCallbackProps) => Promise<void>;
|
||||
}
|
||||
};
|
||||
|
||||
export function createDocumentHandler<T extends ArtifactKind>(config: {
|
||||
kind: T;
|
||||
|
|
@ -89,10 +89,10 @@ export function createDocumentHandler<T extends ArtifactKind>(config: {
|
|||
/*
|
||||
* Use this array to define the document handlers for each artifact kind.
|
||||
*/
|
||||
export const documentHandlersByArtifactKind: Array<DocumentHandler> = [
|
||||
export const documentHandlersByArtifactKind: DocumentHandler[] = [
|
||||
textDocumentHandler,
|
||||
codeDocumentHandler,
|
||||
sheetDocumentHandler,
|
||||
];
|
||||
|
||||
export const artifactKinds = ['text', 'code', 'sheet'] as const;
|
||||
export const artifactKinds = ["text", "code", "sheet"] as const;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { generateDummyPassword } from './db/utils';
|
||||
import { generateDummyPassword } from "./db/utils";
|
||||
|
||||
export const isProductionEnvironment = process.env.NODE_ENV === 'production';
|
||||
export const isDevelopmentEnvironment = process.env.NODE_ENV === 'development';
|
||||
export const isProductionEnvironment = process.env.NODE_ENV === "production";
|
||||
export const isDevelopmentEnvironment = process.env.NODE_ENV === "development";
|
||||
export const isTestEnvironment = Boolean(
|
||||
process.env.PLAYWRIGHT_TEST_BASE_URL ||
|
||||
process.env.PLAYWRIGHT ||
|
||||
process.env.CI_PLAYWRIGHT,
|
||||
process.env.CI_PLAYWRIGHT
|
||||
);
|
||||
|
||||
export const guestRegex = /^guest-\d+$/;
|
||||
|
|
|
|||
|
|
@ -1,32 +1,32 @@
|
|||
import { config } from 'dotenv';
|
||||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import { migrate } from 'drizzle-orm/postgres-js/migrator';
|
||||
import postgres from 'postgres';
|
||||
import { config } from "dotenv";
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import { migrate } from "drizzle-orm/postgres-js/migrator";
|
||||
import postgres from "postgres";
|
||||
|
||||
config({
|
||||
path: '.env.local',
|
||||
path: ".env.local",
|
||||
});
|
||||
|
||||
const runMigrate = async () => {
|
||||
if (!process.env.POSTGRES_URL) {
|
||||
throw new Error('POSTGRES_URL is not defined');
|
||||
throw new Error("POSTGRES_URL is not defined");
|
||||
}
|
||||
|
||||
const connection = postgres(process.env.POSTGRES_URL, { max: 1 });
|
||||
const db = drizzle(connection);
|
||||
|
||||
console.log('⏳ Running migrations...');
|
||||
console.log("⏳ Running migrations...");
|
||||
|
||||
const start = Date.now();
|
||||
await migrate(db, { migrationsFolder: './lib/db/migrations' });
|
||||
await migrate(db, { migrationsFolder: "./lib/db/migrations" });
|
||||
const end = Date.now();
|
||||
|
||||
console.log('✅ Migrations completed in', end - start, 'ms');
|
||||
console.log("✅ Migrations completed in", end - start, "ms");
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
runMigrate().catch((err) => {
|
||||
console.error('❌ Migration failed');
|
||||
console.error("❌ Migration failed");
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -40,12 +40,8 @@
|
|||
"name": "Chat_userId_User_id_fk",
|
||||
"tableFrom": "Chat",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -91,4 +87,4 @@
|
|||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,12 +71,8 @@
|
|||
"name": "Suggestion_userId_User_id_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -84,14 +80,8 @@
|
|||
"name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "Document",
|
||||
"columnsFrom": [
|
||||
"documentId",
|
||||
"documentCreatedAt"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id",
|
||||
"createdAt"
|
||||
],
|
||||
"columnsFrom": ["documentId", "documentCreatedAt"],
|
||||
"columnsTo": ["id", "createdAt"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -99,9 +89,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Suggestion_id_pk": {
|
||||
"name": "Suggestion_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -142,12 +130,8 @@
|
|||
"name": "Chat_userId_User_id_fk",
|
||||
"tableFrom": "Chat",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -197,12 +181,8 @@
|
|||
"name": "Document_userId_User_id_fk",
|
||||
"tableFrom": "Document",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -210,10 +190,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Document_id_createdAt_pk": {
|
||||
"name": "Document_id_createdAt_pk",
|
||||
"columns": [
|
||||
"id",
|
||||
"createdAt"
|
||||
]
|
||||
"columns": ["id", "createdAt"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -256,4 +233,4 @@
|
|||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,12 +71,8 @@
|
|||
"name": "Suggestion_userId_User_id_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -84,14 +80,8 @@
|
|||
"name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "Document",
|
||||
"columnsFrom": [
|
||||
"documentId",
|
||||
"documentCreatedAt"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id",
|
||||
"createdAt"
|
||||
],
|
||||
"columnsFrom": ["documentId", "documentCreatedAt"],
|
||||
"columnsTo": ["id", "createdAt"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -99,9 +89,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Suggestion_id_pk": {
|
||||
"name": "Suggestion_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -142,12 +130,8 @@
|
|||
"name": "Chat_userId_User_id_fk",
|
||||
"tableFrom": "Chat",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -197,12 +181,8 @@
|
|||
"name": "Document_userId_User_id_fk",
|
||||
"tableFrom": "Document",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -210,10 +190,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Document_id_createdAt_pk": {
|
||||
"name": "Document_id_createdAt_pk",
|
||||
"columns": [
|
||||
"id",
|
||||
"createdAt"
|
||||
]
|
||||
"columns": ["id", "createdAt"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -260,12 +237,8 @@
|
|||
"name": "Message_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -331,12 +304,8 @@
|
|||
"name": "Vote_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -344,12 +313,8 @@
|
|||
"name": "Vote_messageId_Message_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Message",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["messageId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -357,10 +322,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Vote_chatId_messageId_pk": {
|
||||
"name": "Vote_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
"columns": ["chatId", "messageId"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -374,4 +336,4 @@
|
|||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,12 +47,8 @@
|
|||
"name": "Chat_userId_User_id_fk",
|
||||
"tableFrom": "Chat",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -102,12 +98,8 @@
|
|||
"name": "Document_userId_User_id_fk",
|
||||
"tableFrom": "Document",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -115,10 +107,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Document_id_createdAt_pk": {
|
||||
"name": "Document_id_createdAt_pk",
|
||||
"columns": [
|
||||
"id",
|
||||
"createdAt"
|
||||
]
|
||||
"columns": ["id", "createdAt"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -165,12 +154,8 @@
|
|||
"name": "Message_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -245,12 +230,8 @@
|
|||
"name": "Suggestion_userId_User_id_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -258,14 +239,8 @@
|
|||
"name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "Document",
|
||||
"columnsFrom": [
|
||||
"documentId",
|
||||
"documentCreatedAt"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id",
|
||||
"createdAt"
|
||||
],
|
||||
"columnsFrom": ["documentId", "documentCreatedAt"],
|
||||
"columnsTo": ["id", "createdAt"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -273,9 +248,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Suggestion_id_pk": {
|
||||
"name": "Suggestion_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -338,12 +311,8 @@
|
|||
"name": "Vote_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -351,12 +320,8 @@
|
|||
"name": "Vote_messageId_Message_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Message",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["messageId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -364,10 +329,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Vote_chatId_messageId_pk": {
|
||||
"name": "Vote_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
"columns": ["chatId", "messageId"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -381,4 +343,4 @@
|
|||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,12 +47,8 @@
|
|||
"name": "Chat_userId_User_id_fk",
|
||||
"tableFrom": "Chat",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -109,12 +105,8 @@
|
|||
"name": "Document_userId_User_id_fk",
|
||||
"tableFrom": "Document",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -122,10 +114,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Document_id_createdAt_pk": {
|
||||
"name": "Document_id_createdAt_pk",
|
||||
"columns": [
|
||||
"id",
|
||||
"createdAt"
|
||||
]
|
||||
"columns": ["id", "createdAt"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -172,12 +161,8 @@
|
|||
"name": "Message_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -252,12 +237,8 @@
|
|||
"name": "Suggestion_userId_User_id_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -265,14 +246,8 @@
|
|||
"name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "Document",
|
||||
"columnsFrom": [
|
||||
"documentId",
|
||||
"documentCreatedAt"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id",
|
||||
"createdAt"
|
||||
],
|
||||
"columnsFrom": ["documentId", "documentCreatedAt"],
|
||||
"columnsTo": ["id", "createdAt"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -280,9 +255,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Suggestion_id_pk": {
|
||||
"name": "Suggestion_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -345,12 +318,8 @@
|
|||
"name": "Vote_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -358,12 +327,8 @@
|
|||
"name": "Vote_messageId_Message_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Message",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["messageId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -371,10 +336,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Vote_chatId_messageId_pk": {
|
||||
"name": "Vote_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
"columns": ["chatId", "messageId"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -388,4 +350,4 @@
|
|||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,12 +47,8 @@
|
|||
"name": "Chat_userId_User_id_fk",
|
||||
"tableFrom": "Chat",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -109,12 +105,8 @@
|
|||
"name": "Document_userId_User_id_fk",
|
||||
"tableFrom": "Document",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -122,10 +114,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Document_id_createdAt_pk": {
|
||||
"name": "Document_id_createdAt_pk",
|
||||
"columns": [
|
||||
"id",
|
||||
"createdAt"
|
||||
]
|
||||
"columns": ["id", "createdAt"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -178,12 +167,8 @@
|
|||
"name": "Message_v2_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message_v2",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -233,12 +218,8 @@
|
|||
"name": "Message_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -313,12 +294,8 @@
|
|||
"name": "Suggestion_userId_User_id_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -326,14 +303,8 @@
|
|||
"name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "Document",
|
||||
"columnsFrom": [
|
||||
"documentId",
|
||||
"documentCreatedAt"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id",
|
||||
"createdAt"
|
||||
],
|
||||
"columnsFrom": ["documentId", "documentCreatedAt"],
|
||||
"columnsTo": ["id", "createdAt"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -341,9 +312,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Suggestion_id_pk": {
|
||||
"name": "Suggestion_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -406,12 +375,8 @@
|
|||
"name": "Vote_v2_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote_v2",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -419,12 +384,8 @@
|
|||
"name": "Vote_v2_messageId_Message_v2_id_fk",
|
||||
"tableFrom": "Vote_v2",
|
||||
"tableTo": "Message_v2",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["messageId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -432,10 +393,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Vote_v2_chatId_messageId_pk": {
|
||||
"name": "Vote_v2_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
"columns": ["chatId", "messageId"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -469,12 +427,8 @@
|
|||
"name": "Vote_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -482,12 +436,8 @@
|
|||
"name": "Vote_messageId_Message_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Message",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["messageId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -495,10 +445,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Vote_chatId_messageId_pk": {
|
||||
"name": "Vote_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
"columns": ["chatId", "messageId"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -512,4 +459,4 @@
|
|||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,12 +47,8 @@
|
|||
"name": "Chat_userId_User_id_fk",
|
||||
"tableFrom": "Chat",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -109,12 +105,8 @@
|
|||
"name": "Document_userId_User_id_fk",
|
||||
"tableFrom": "Document",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -122,10 +114,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Document_id_createdAt_pk": {
|
||||
"name": "Document_id_createdAt_pk",
|
||||
"columns": [
|
||||
"id",
|
||||
"createdAt"
|
||||
]
|
||||
"columns": ["id", "createdAt"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -178,12 +167,8 @@
|
|||
"name": "Message_v2_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message_v2",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -233,12 +218,8 @@
|
|||
"name": "Message_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -276,12 +257,8 @@
|
|||
"name": "Stream_chatId_Chat_id_fk",
|
||||
"tableFrom": "Stream",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -289,9 +266,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Stream_id_pk": {
|
||||
"name": "Stream_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -363,12 +338,8 @@
|
|||
"name": "Suggestion_userId_User_id_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -376,14 +347,8 @@
|
|||
"name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "Document",
|
||||
"columnsFrom": [
|
||||
"documentId",
|
||||
"documentCreatedAt"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id",
|
||||
"createdAt"
|
||||
],
|
||||
"columnsFrom": ["documentId", "documentCreatedAt"],
|
||||
"columnsTo": ["id", "createdAt"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -391,9 +356,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Suggestion_id_pk": {
|
||||
"name": "Suggestion_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -456,12 +419,8 @@
|
|||
"name": "Vote_v2_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote_v2",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -469,12 +428,8 @@
|
|||
"name": "Vote_v2_messageId_Message_v2_id_fk",
|
||||
"tableFrom": "Vote_v2",
|
||||
"tableTo": "Message_v2",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["messageId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -482,10 +437,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Vote_v2_chatId_messageId_pk": {
|
||||
"name": "Vote_v2_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
"columns": ["chatId", "messageId"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -519,12 +471,8 @@
|
|||
"name": "Vote_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -532,12 +480,8 @@
|
|||
"name": "Vote_messageId_Message_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Message",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["messageId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -545,10 +489,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Vote_chatId_messageId_pk": {
|
||||
"name": "Vote_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
"columns": ["chatId", "messageId"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -562,4 +503,4 @@
|
|||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,12 +53,8 @@
|
|||
"name": "Chat_userId_User_id_fk",
|
||||
"tableFrom": "Chat",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -115,12 +111,8 @@
|
|||
"name": "Document_userId_User_id_fk",
|
||||
"tableFrom": "Document",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -128,10 +120,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Document_id_createdAt_pk": {
|
||||
"name": "Document_id_createdAt_pk",
|
||||
"columns": [
|
||||
"id",
|
||||
"createdAt"
|
||||
]
|
||||
"columns": ["id", "createdAt"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -184,12 +173,8 @@
|
|||
"name": "Message_v2_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message_v2",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -239,12 +224,8 @@
|
|||
"name": "Message_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -282,12 +263,8 @@
|
|||
"name": "Stream_chatId_Chat_id_fk",
|
||||
"tableFrom": "Stream",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -295,9 +272,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Stream_id_pk": {
|
||||
"name": "Stream_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -369,12 +344,8 @@
|
|||
"name": "Suggestion_userId_User_id_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["userId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -382,14 +353,8 @@
|
|||
"name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "Document",
|
||||
"columnsFrom": [
|
||||
"documentId",
|
||||
"documentCreatedAt"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id",
|
||||
"createdAt"
|
||||
],
|
||||
"columnsFrom": ["documentId", "documentCreatedAt"],
|
||||
"columnsTo": ["id", "createdAt"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -397,9 +362,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Suggestion_id_pk": {
|
||||
"name": "Suggestion_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -462,12 +425,8 @@
|
|||
"name": "Vote_v2_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote_v2",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -475,12 +434,8 @@
|
|||
"name": "Vote_v2_messageId_Message_v2_id_fk",
|
||||
"tableFrom": "Vote_v2",
|
||||
"tableTo": "Message_v2",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["messageId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -488,10 +443,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Vote_v2_chatId_messageId_pk": {
|
||||
"name": "Vote_v2_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
"columns": ["chatId", "messageId"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -525,12 +477,8 @@
|
|||
"name": "Vote_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["chatId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
|
|
@ -538,12 +486,8 @@
|
|||
"name": "Vote_messageId_Message_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Message",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"columnsFrom": ["messageId"],
|
||||
"columnsTo": ["id"],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
|
|
@ -551,10 +495,7 @@
|
|||
"compositePrimaryKeys": {
|
||||
"Vote_chatId_messageId_pk": {
|
||||
"name": "Vote_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
"columns": ["chatId", "messageId"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
|
|
@ -568,4 +509,4 @@
|
|||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,4 +59,4 @@
|
|||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import 'server-only';
|
||||
import "server-only";
|
||||
|
||||
import {
|
||||
and,
|
||||
|
|
@ -11,29 +11,28 @@ import {
|
|||
inArray,
|
||||
lt,
|
||||
type SQL,
|
||||
} from 'drizzle-orm';
|
||||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import postgres from 'postgres';
|
||||
|
||||
} from "drizzle-orm";
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
import type { ArtifactKind } from "@/components/artifact";
|
||||
import type { VisibilityType } from "@/components/visibility-selector";
|
||||
import { ChatSDKError } from "../errors";
|
||||
import type { AppUsage } from "../usage";
|
||||
import { generateUUID } from "../utils";
|
||||
import {
|
||||
user,
|
||||
chat,
|
||||
type User,
|
||||
document,
|
||||
type Suggestion,
|
||||
suggestion,
|
||||
message,
|
||||
vote,
|
||||
type DBMessage,
|
||||
type Chat,
|
||||
chat,
|
||||
type DBMessage,
|
||||
document,
|
||||
message,
|
||||
type Suggestion,
|
||||
stream,
|
||||
} from './schema';
|
||||
import type { ArtifactKind } from '@/components/artifact';
|
||||
import { generateUUID } from '../utils';
|
||||
import { generateHashedPassword } from './utils';
|
||||
import type { VisibilityType } from '@/components/visibility-selector';
|
||||
import { ChatSDKError } from '../errors';
|
||||
import type { AppUsage } from '../usage';
|
||||
suggestion,
|
||||
type User,
|
||||
user,
|
||||
vote,
|
||||
} from "./schema";
|
||||
import { generateHashedPassword } from "./utils";
|
||||
|
||||
// Optionally, if not using email/pass login, you can
|
||||
// use the Drizzle adapter for Auth.js / NextAuth
|
||||
|
|
@ -43,13 +42,13 @@ import type { AppUsage } from '../usage';
|
|||
const client = postgres(process.env.POSTGRES_URL!);
|
||||
const db = drizzle(client);
|
||||
|
||||
export async function getUser(email: string): Promise<Array<User>> {
|
||||
export async function getUser(email: string): Promise<User[]> {
|
||||
try {
|
||||
return await db.select().from(user).where(eq(user.email, email));
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get user by email',
|
||||
"bad_request:database",
|
||||
"Failed to get user by email"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -59,8 +58,8 @@ export async function createUser(email: string, password: string) {
|
|||
|
||||
try {
|
||||
return await db.insert(user).values({ email, password: hashedPassword });
|
||||
} catch (error) {
|
||||
throw new ChatSDKError('bad_request:database', 'Failed to create user');
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError("bad_request:database", "Failed to create user");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,10 +72,10 @@ export async function createGuestUser() {
|
|||
id: user.id,
|
||||
email: user.email,
|
||||
});
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to create guest user',
|
||||
"bad_request:database",
|
||||
"Failed to create guest user"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -100,8 +99,8 @@ export async function saveChat({
|
|||
title,
|
||||
visibility,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new ChatSDKError('bad_request:database', 'Failed to save chat');
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError("bad_request:database", "Failed to save chat");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,10 +115,10 @@ export async function deleteChatById({ id }: { id: string }) {
|
|||
.where(eq(chat.id, id))
|
||||
.returning();
|
||||
return chatsDeleted;
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to delete chat by id',
|
||||
"bad_request:database",
|
||||
"Failed to delete chat by id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -145,12 +144,12 @@ export async function getChatsByUserId({
|
|||
.where(
|
||||
whereCondition
|
||||
? and(whereCondition, eq(chat.userId, id))
|
||||
: eq(chat.userId, id),
|
||||
: eq(chat.userId, id)
|
||||
)
|
||||
.orderBy(desc(chat.createdAt))
|
||||
.limit(extendedLimit);
|
||||
|
||||
let filteredChats: Array<Chat> = [];
|
||||
let filteredChats: Chat[] = [];
|
||||
|
||||
if (startingAfter) {
|
||||
const [selectedChat] = await db
|
||||
|
|
@ -161,8 +160,8 @@ export async function getChatsByUserId({
|
|||
|
||||
if (!selectedChat) {
|
||||
throw new ChatSDKError(
|
||||
'not_found:database',
|
||||
`Chat with id ${startingAfter} not found`,
|
||||
"not_found:database",
|
||||
`Chat with id ${startingAfter} not found`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -176,8 +175,8 @@ export async function getChatsByUserId({
|
|||
|
||||
if (!selectedChat) {
|
||||
throw new ChatSDKError(
|
||||
'not_found:database',
|
||||
`Chat with id ${endingBefore} not found`,
|
||||
"not_found:database",
|
||||
`Chat with id ${endingBefore} not found`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -192,10 +191,10 @@ export async function getChatsByUserId({
|
|||
chats: hasMore ? filteredChats.slice(0, limit) : filteredChats,
|
||||
hasMore,
|
||||
};
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get chats by user id',
|
||||
"bad_request:database",
|
||||
"Failed to get chats by user id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -208,20 +207,16 @@ export async function getChatById({ id }: { id: string }) {
|
|||
}
|
||||
|
||||
return selectedChat;
|
||||
} catch (error) {
|
||||
throw new ChatSDKError('bad_request:database', 'Failed to get chat by id');
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError("bad_request:database", "Failed to get chat by id");
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveMessages({
|
||||
messages,
|
||||
}: {
|
||||
messages: Array<DBMessage>;
|
||||
}) {
|
||||
export async function saveMessages({ messages }: { messages: DBMessage[] }) {
|
||||
try {
|
||||
return await db.insert(message).values(messages);
|
||||
} catch (error) {
|
||||
throw new ChatSDKError('bad_request:database', 'Failed to save messages');
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError("bad_request:database", "Failed to save messages");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,10 +227,10 @@ export async function getMessagesByChatId({ id }: { id: string }) {
|
|||
.from(message)
|
||||
.where(eq(message.chatId, id))
|
||||
.orderBy(asc(message.createdAt));
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get messages by chat id',
|
||||
"bad_request:database",
|
||||
"Failed to get messages by chat id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -247,7 +242,7 @@ export async function voteMessage({
|
|||
}: {
|
||||
chatId: string;
|
||||
messageId: string;
|
||||
type: 'up' | 'down';
|
||||
type: "up" | "down";
|
||||
}) {
|
||||
try {
|
||||
const [existingVote] = await db
|
||||
|
|
@ -258,26 +253,26 @@ export async function voteMessage({
|
|||
if (existingVote) {
|
||||
return await db
|
||||
.update(vote)
|
||||
.set({ isUpvoted: type === 'up' })
|
||||
.set({ isUpvoted: type === "up" })
|
||||
.where(and(eq(vote.messageId, messageId), eq(vote.chatId, chatId)));
|
||||
}
|
||||
return await db.insert(vote).values({
|
||||
chatId,
|
||||
messageId,
|
||||
isUpvoted: type === 'up',
|
||||
isUpvoted: type === "up",
|
||||
});
|
||||
} catch (error) {
|
||||
throw new ChatSDKError('bad_request:database', 'Failed to vote message');
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError("bad_request:database", "Failed to vote message");
|
||||
}
|
||||
}
|
||||
|
||||
export async function getVotesByChatId({ id }: { id: string }) {
|
||||
try {
|
||||
return await db.select().from(vote).where(eq(vote.chatId, id));
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get votes by chat id',
|
||||
"bad_request:database",
|
||||
"Failed to get votes by chat id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -307,8 +302,8 @@ export async function saveDocument({
|
|||
createdAt: new Date(),
|
||||
})
|
||||
.returning();
|
||||
} catch (error) {
|
||||
throw new ChatSDKError('bad_request:database', 'Failed to save document');
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError("bad_request:database", "Failed to save document");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -321,10 +316,10 @@ export async function getDocumentsById({ id }: { id: string }) {
|
|||
.orderBy(asc(document.createdAt));
|
||||
|
||||
return documents;
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get documents by id',
|
||||
"bad_request:database",
|
||||
"Failed to get documents by id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -338,10 +333,10 @@ export async function getDocumentById({ id }: { id: string }) {
|
|||
.orderBy(desc(document.createdAt));
|
||||
|
||||
return selectedDocument;
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get document by id',
|
||||
"bad_request:database",
|
||||
"Failed to get document by id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -359,18 +354,18 @@ export async function deleteDocumentsByIdAfterTimestamp({
|
|||
.where(
|
||||
and(
|
||||
eq(suggestion.documentId, id),
|
||||
gt(suggestion.documentCreatedAt, timestamp),
|
||||
),
|
||||
gt(suggestion.documentCreatedAt, timestamp)
|
||||
)
|
||||
);
|
||||
|
||||
return await db
|
||||
.delete(document)
|
||||
.where(and(eq(document.id, id), gt(document.createdAt, timestamp)))
|
||||
.returning();
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to delete documents by id after timestamp',
|
||||
"bad_request:database",
|
||||
"Failed to delete documents by id after timestamp"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -378,14 +373,14 @@ export async function deleteDocumentsByIdAfterTimestamp({
|
|||
export async function saveSuggestions({
|
||||
suggestions,
|
||||
}: {
|
||||
suggestions: Array<Suggestion>;
|
||||
suggestions: Suggestion[];
|
||||
}) {
|
||||
try {
|
||||
return await db.insert(suggestion).values(suggestions);
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to save suggestions',
|
||||
"bad_request:database",
|
||||
"Failed to save suggestions"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -400,10 +395,10 @@ export async function getSuggestionsByDocumentId({
|
|||
.select()
|
||||
.from(suggestion)
|
||||
.where(and(eq(suggestion.documentId, documentId)));
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get suggestions by document id',
|
||||
"bad_request:database",
|
||||
"Failed to get suggestions by document id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -411,10 +406,10 @@ export async function getSuggestionsByDocumentId({
|
|||
export async function getMessageById({ id }: { id: string }) {
|
||||
try {
|
||||
return await db.select().from(message).where(eq(message.id, id));
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get message by id',
|
||||
"bad_request:database",
|
||||
"Failed to get message by id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -431,28 +426,30 @@ export async function deleteMessagesByChatIdAfterTimestamp({
|
|||
.select({ id: message.id })
|
||||
.from(message)
|
||||
.where(
|
||||
and(eq(message.chatId, chatId), gte(message.createdAt, timestamp)),
|
||||
and(eq(message.chatId, chatId), gte(message.createdAt, timestamp))
|
||||
);
|
||||
|
||||
const messageIds = messagesToDelete.map((message) => message.id);
|
||||
const messageIds = messagesToDelete.map(
|
||||
(currentMessage) => currentMessage.id
|
||||
);
|
||||
|
||||
if (messageIds.length > 0) {
|
||||
await db
|
||||
.delete(vote)
|
||||
.where(
|
||||
and(eq(vote.chatId, chatId), inArray(vote.messageId, messageIds)),
|
||||
and(eq(vote.chatId, chatId), inArray(vote.messageId, messageIds))
|
||||
);
|
||||
|
||||
return await db
|
||||
.delete(message)
|
||||
.where(
|
||||
and(eq(message.chatId, chatId), inArray(message.id, messageIds)),
|
||||
and(eq(message.chatId, chatId), inArray(message.id, messageIds))
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to delete messages by chat id after timestamp',
|
||||
"bad_request:database",
|
||||
"Failed to delete messages by chat id after timestamp"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -462,14 +459,14 @@ export async function updateChatVisiblityById({
|
|||
visibility,
|
||||
}: {
|
||||
chatId: string;
|
||||
visibility: 'private' | 'public';
|
||||
visibility: "private" | "public";
|
||||
}) {
|
||||
try {
|
||||
return await db.update(chat).set({ visibility }).where(eq(chat.id, chatId));
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to update chat visibility by id',
|
||||
"bad_request:database",
|
||||
"Failed to update chat visibility by id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -488,7 +485,7 @@ export async function updateChatLastContextById({
|
|||
.set({ lastContext: context })
|
||||
.where(eq(chat.id, chatId));
|
||||
} catch (error) {
|
||||
console.warn('Failed to update lastContext for chat', chatId, error);
|
||||
console.warn("Failed to update lastContext for chat", chatId, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -502,7 +499,7 @@ export async function getMessageCountByUserId({
|
|||
}) {
|
||||
try {
|
||||
const twentyFourHoursAgo = new Date(
|
||||
Date.now() - differenceInHours * 60 * 60 * 1000,
|
||||
Date.now() - differenceInHours * 60 * 60 * 1000
|
||||
);
|
||||
|
||||
const [stats] = await db
|
||||
|
|
@ -513,16 +510,16 @@ export async function getMessageCountByUserId({
|
|||
and(
|
||||
eq(chat.userId, id),
|
||||
gte(message.createdAt, twentyFourHoursAgo),
|
||||
eq(message.role, 'user'),
|
||||
),
|
||||
eq(message.role, "user")
|
||||
)
|
||||
)
|
||||
.execute();
|
||||
|
||||
return stats?.count ?? 0;
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get message count by user id',
|
||||
"bad_request:database",
|
||||
"Failed to get message count by user id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -538,10 +535,10 @@ export async function createStreamId({
|
|||
await db
|
||||
.insert(stream)
|
||||
.values({ id: streamId, chatId, createdAt: new Date() });
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to create stream id',
|
||||
"bad_request:database",
|
||||
"Failed to create stream id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -556,10 +553,10 @@ export async function getStreamIdsByChatId({ chatId }: { chatId: string }) {
|
|||
.execute();
|
||||
|
||||
return streamIds.map(({ id }) => id);
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
throw new ChatSDKError(
|
||||
'bad_request:database',
|
||||
'Failed to get stream ids by chat id',
|
||||
"bad_request:database",
|
||||
"Failed to get stream ids by chat id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
140
lib/db/schema.ts
140
lib/db/schema.ts
|
|
@ -1,64 +1,64 @@
|
|||
import type { InferSelectModel } from 'drizzle-orm';
|
||||
import type { InferSelectModel } from "drizzle-orm";
|
||||
import {
|
||||
pgTable,
|
||||
varchar,
|
||||
timestamp,
|
||||
boolean,
|
||||
foreignKey,
|
||||
json,
|
||||
jsonb,
|
||||
uuid,
|
||||
text,
|
||||
pgTable,
|
||||
primaryKey,
|
||||
foreignKey,
|
||||
boolean,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import type { AppUsage } from '../usage';
|
||||
text,
|
||||
timestamp,
|
||||
uuid,
|
||||
varchar,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import type { AppUsage } from "../usage";
|
||||
|
||||
export const user = pgTable('User', {
|
||||
id: uuid('id').primaryKey().notNull().defaultRandom(),
|
||||
email: varchar('email', { length: 64 }).notNull(),
|
||||
password: varchar('password', { length: 64 }),
|
||||
export const user = pgTable("User", {
|
||||
id: uuid("id").primaryKey().notNull().defaultRandom(),
|
||||
email: varchar("email", { length: 64 }).notNull(),
|
||||
password: varchar("password", { length: 64 }),
|
||||
});
|
||||
|
||||
export type User = InferSelectModel<typeof user>;
|
||||
|
||||
export const chat = pgTable('Chat', {
|
||||
id: uuid('id').primaryKey().notNull().defaultRandom(),
|
||||
createdAt: timestamp('createdAt').notNull(),
|
||||
title: text('title').notNull(),
|
||||
userId: uuid('userId')
|
||||
export const chat = pgTable("Chat", {
|
||||
id: uuid("id").primaryKey().notNull().defaultRandom(),
|
||||
createdAt: timestamp("createdAt").notNull(),
|
||||
title: text("title").notNull(),
|
||||
userId: uuid("userId")
|
||||
.notNull()
|
||||
.references(() => user.id),
|
||||
visibility: varchar('visibility', { enum: ['public', 'private'] })
|
||||
visibility: varchar("visibility", { enum: ["public", "private"] })
|
||||
.notNull()
|
||||
.default('private'),
|
||||
lastContext: jsonb('lastContext').$type<AppUsage | null>(),
|
||||
.default("private"),
|
||||
lastContext: jsonb("lastContext").$type<AppUsage | null>(),
|
||||
});
|
||||
|
||||
export type Chat = InferSelectModel<typeof chat>;
|
||||
|
||||
// DEPRECATED: The following schema is deprecated and will be removed in the future.
|
||||
// Read the migration guide at https://chat-sdk.dev/docs/migration-guides/message-parts
|
||||
export const messageDeprecated = pgTable('Message', {
|
||||
id: uuid('id').primaryKey().notNull().defaultRandom(),
|
||||
chatId: uuid('chatId')
|
||||
export const messageDeprecated = pgTable("Message", {
|
||||
id: uuid("id").primaryKey().notNull().defaultRandom(),
|
||||
chatId: uuid("chatId")
|
||||
.notNull()
|
||||
.references(() => chat.id),
|
||||
role: varchar('role').notNull(),
|
||||
content: json('content').notNull(),
|
||||
createdAt: timestamp('createdAt').notNull(),
|
||||
role: varchar("role").notNull(),
|
||||
content: json("content").notNull(),
|
||||
createdAt: timestamp("createdAt").notNull(),
|
||||
});
|
||||
|
||||
export type MessageDeprecated = InferSelectModel<typeof messageDeprecated>;
|
||||
|
||||
export const message = pgTable('Message_v2', {
|
||||
id: uuid('id').primaryKey().notNull().defaultRandom(),
|
||||
chatId: uuid('chatId')
|
||||
export const message = pgTable("Message_v2", {
|
||||
id: uuid("id").primaryKey().notNull().defaultRandom(),
|
||||
chatId: uuid("chatId")
|
||||
.notNull()
|
||||
.references(() => chat.id),
|
||||
role: varchar('role').notNull(),
|
||||
parts: json('parts').notNull(),
|
||||
attachments: json('attachments').notNull(),
|
||||
createdAt: timestamp('createdAt').notNull(),
|
||||
role: varchar("role").notNull(),
|
||||
parts: json("parts").notNull(),
|
||||
attachments: json("attachments").notNull(),
|
||||
createdAt: timestamp("createdAt").notNull(),
|
||||
});
|
||||
|
||||
export type DBMessage = InferSelectModel<typeof message>;
|
||||
|
|
@ -66,56 +66,56 @@ export type DBMessage = InferSelectModel<typeof message>;
|
|||
// DEPRECATED: The following schema is deprecated and will be removed in the future.
|
||||
// Read the migration guide at https://chat-sdk.dev/docs/migration-guides/message-parts
|
||||
export const voteDeprecated = pgTable(
|
||||
'Vote',
|
||||
"Vote",
|
||||
{
|
||||
chatId: uuid('chatId')
|
||||
chatId: uuid("chatId")
|
||||
.notNull()
|
||||
.references(() => chat.id),
|
||||
messageId: uuid('messageId')
|
||||
messageId: uuid("messageId")
|
||||
.notNull()
|
||||
.references(() => messageDeprecated.id),
|
||||
isUpvoted: boolean('isUpvoted').notNull(),
|
||||
isUpvoted: boolean("isUpvoted").notNull(),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
pk: primaryKey({ columns: [table.chatId, table.messageId] }),
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export type VoteDeprecated = InferSelectModel<typeof voteDeprecated>;
|
||||
|
||||
export const vote = pgTable(
|
||||
'Vote_v2',
|
||||
"Vote_v2",
|
||||
{
|
||||
chatId: uuid('chatId')
|
||||
chatId: uuid("chatId")
|
||||
.notNull()
|
||||
.references(() => chat.id),
|
||||
messageId: uuid('messageId')
|
||||
messageId: uuid("messageId")
|
||||
.notNull()
|
||||
.references(() => message.id),
|
||||
isUpvoted: boolean('isUpvoted').notNull(),
|
||||
isUpvoted: boolean("isUpvoted").notNull(),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
pk: primaryKey({ columns: [table.chatId, table.messageId] }),
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export type Vote = InferSelectModel<typeof vote>;
|
||||
|
||||
export const document = pgTable(
|
||||
'Document',
|
||||
"Document",
|
||||
{
|
||||
id: uuid('id').notNull().defaultRandom(),
|
||||
createdAt: timestamp('createdAt').notNull(),
|
||||
title: text('title').notNull(),
|
||||
content: text('content'),
|
||||
kind: varchar('text', { enum: ['text', 'code', 'image', 'sheet'] })
|
||||
id: uuid("id").notNull().defaultRandom(),
|
||||
createdAt: timestamp("createdAt").notNull(),
|
||||
title: text("title").notNull(),
|
||||
content: text("content"),
|
||||
kind: varchar("text", { enum: ["text", "code", "image", "sheet"] })
|
||||
.notNull()
|
||||
.default('text'),
|
||||
userId: uuid('userId')
|
||||
.default("text"),
|
||||
userId: uuid("userId")
|
||||
.notNull()
|
||||
.references(() => user.id),
|
||||
},
|
||||
|
|
@ -123,25 +123,25 @@ export const document = pgTable(
|
|||
return {
|
||||
pk: primaryKey({ columns: [table.id, table.createdAt] }),
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export type Document = InferSelectModel<typeof document>;
|
||||
|
||||
export const suggestion = pgTable(
|
||||
'Suggestion',
|
||||
"Suggestion",
|
||||
{
|
||||
id: uuid('id').notNull().defaultRandom(),
|
||||
documentId: uuid('documentId').notNull(),
|
||||
documentCreatedAt: timestamp('documentCreatedAt').notNull(),
|
||||
originalText: text('originalText').notNull(),
|
||||
suggestedText: text('suggestedText').notNull(),
|
||||
description: text('description'),
|
||||
isResolved: boolean('isResolved').notNull().default(false),
|
||||
userId: uuid('userId')
|
||||
id: uuid("id").notNull().defaultRandom(),
|
||||
documentId: uuid("documentId").notNull(),
|
||||
documentCreatedAt: timestamp("documentCreatedAt").notNull(),
|
||||
originalText: text("originalText").notNull(),
|
||||
suggestedText: text("suggestedText").notNull(),
|
||||
description: text("description"),
|
||||
isResolved: boolean("isResolved").notNull().default(false),
|
||||
userId: uuid("userId")
|
||||
.notNull()
|
||||
.references(() => user.id),
|
||||
createdAt: timestamp('createdAt').notNull(),
|
||||
createdAt: timestamp("createdAt").notNull(),
|
||||
},
|
||||
(table) => ({
|
||||
pk: primaryKey({ columns: [table.id] }),
|
||||
|
|
@ -149,17 +149,17 @@ export const suggestion = pgTable(
|
|||
columns: [table.documentId, table.documentCreatedAt],
|
||||
foreignColumns: [document.id, document.createdAt],
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
export type Suggestion = InferSelectModel<typeof suggestion>;
|
||||
|
||||
export const stream = pgTable(
|
||||
'Stream',
|
||||
"Stream",
|
||||
{
|
||||
id: uuid('id').notNull().defaultRandom(),
|
||||
chatId: uuid('chatId').notNull(),
|
||||
createdAt: timestamp('createdAt').notNull(),
|
||||
id: uuid("id").notNull().defaultRandom(),
|
||||
chatId: uuid("chatId").notNull(),
|
||||
createdAt: timestamp("createdAt").notNull(),
|
||||
},
|
||||
(table) => ({
|
||||
pk: primaryKey({ columns: [table.id] }),
|
||||
|
|
@ -167,7 +167,7 @@ export const stream = pgTable(
|
|||
columns: [table.chatId],
|
||||
foreignColumns: [chat.id],
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
export type Stream = InferSelectModel<typeof stream>;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { generateId } from 'ai';
|
||||
import { genSaltSync, hashSync } from 'bcrypt-ts';
|
||||
import { generateId } from "ai";
|
||||
import { genSaltSync, hashSync } from "bcrypt-ts";
|
||||
|
||||
export function generateHashedPassword(password: string) {
|
||||
const salt = genSaltSync(10);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { textblockTypeInputRule } from 'prosemirror-inputrules';
|
||||
import { Schema } from 'prosemirror-model';
|
||||
import { schema } from 'prosemirror-schema-basic';
|
||||
import { addListNodes } from 'prosemirror-schema-list';
|
||||
import type { Transaction } from 'prosemirror-state';
|
||||
import type { EditorView } from 'prosemirror-view';
|
||||
import type { MutableRefObject } from 'react';
|
||||
import { textblockTypeInputRule } from "prosemirror-inputrules";
|
||||
import { Schema } from "prosemirror-model";
|
||||
import { schema } from "prosemirror-schema-basic";
|
||||
import { addListNodes } from "prosemirror-schema-list";
|
||||
import type { Transaction } from "prosemirror-state";
|
||||
import type { EditorView } from "prosemirror-view";
|
||||
import type { MutableRefObject } from "react";
|
||||
|
||||
import { buildContentFromDocument } from './functions';
|
||||
import { buildContentFromDocument } from "./functions";
|
||||
|
||||
export const documentSchema = new Schema({
|
||||
nodes: addListNodes(schema.spec.nodes, 'paragraph block*', 'block'),
|
||||
nodes: addListNodes(schema.spec.nodes, "paragraph block*", "block"),
|
||||
marks: schema.spec.marks,
|
||||
});
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ export function headingRule(level: number) {
|
|||
return textblockTypeInputRule(
|
||||
new RegExp(`^(#{1,${level}})\\s$`),
|
||||
documentSchema.nodes.heading,
|
||||
() => ({ level }),
|
||||
() => ({ level })
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -30,15 +30,17 @@ export const handleTransaction = ({
|
|||
editorRef: MutableRefObject<EditorView | null>;
|
||||
onSaveContent: (updatedContent: string, debounce: boolean) => void;
|
||||
}) => {
|
||||
if (!editorRef || !editorRef.current) return;
|
||||
if (!editorRef || !editorRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newState = editorRef.current.state.apply(transaction);
|
||||
editorRef.current.updateState(newState);
|
||||
|
||||
if (transaction.docChanged && !transaction.getMeta('no-save')) {
|
||||
if (transaction.docChanged && !transaction.getMeta("no-save")) {
|
||||
const updatedContent = buildContentFromDocument(newState.doc);
|
||||
|
||||
if (transaction.getMeta('no-debounce')) {
|
||||
if (transaction.getMeta("no-debounce")) {
|
||||
onSaveContent(updatedContent, false);
|
||||
} else {
|
||||
onSaveContent(updatedContent, true);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Modified from https://github.com/hamflx/prosemirror-diff/blob/master/src/diff.js
|
||||
|
||||
import { diff_match_patch } from 'diff-match-patch';
|
||||
import { Fragment, Node } from 'prosemirror-model';
|
||||
import { diff_match_patch } from "diff-match-patch";
|
||||
import { Fragment, Node } from "prosemirror-model";
|
||||
|
||||
export const DiffType = {
|
||||
Unchanged: 0,
|
||||
|
|
@ -49,7 +49,7 @@ export const patchDocumentNode = (schema, oldNode, newNode) => {
|
|||
const matchedNodes = matchNodes(
|
||||
schema,
|
||||
diffOldChildren,
|
||||
diffNewChildren,
|
||||
diffNewChildren
|
||||
).sort((a, b) => b.count - a.count);
|
||||
const bestMatch = matchedNodes[0];
|
||||
if (bestMatch) {
|
||||
|
|
@ -62,11 +62,11 @@ export const patchDocumentNode = (schema, oldNode, newNode) => {
|
|||
...patchRemainNodes(
|
||||
schema,
|
||||
oldBeforeMatchChildren,
|
||||
newBeforeMatchChildren,
|
||||
),
|
||||
newBeforeMatchChildren
|
||||
)
|
||||
);
|
||||
finalLeftChildren.push(
|
||||
...diffOldChildren.slice(oldStartIndex, oldEndIndex),
|
||||
...diffOldChildren.slice(oldStartIndex, oldEndIndex)
|
||||
);
|
||||
|
||||
const oldAfterMatchChildren = diffOldChildren.slice(oldEndIndex);
|
||||
|
|
@ -76,24 +76,24 @@ export const patchDocumentNode = (schema, oldNode, newNode) => {
|
|||
...patchRemainNodes(
|
||||
schema,
|
||||
oldAfterMatchChildren,
|
||||
newAfterMatchChildren,
|
||||
),
|
||||
newAfterMatchChildren
|
||||
)
|
||||
);
|
||||
} else {
|
||||
finalLeftChildren.push(
|
||||
...patchRemainNodes(schema, diffOldChildren, diffNewChildren),
|
||||
...patchRemainNodes(schema, diffOldChildren, diffNewChildren)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
finalLeftChildren.push(
|
||||
...patchRemainNodes(schema, diffOldChildren, diffNewChildren),
|
||||
...patchRemainNodes(schema, diffOldChildren, diffNewChildren)
|
||||
);
|
||||
}
|
||||
|
||||
return createNewNode(oldNode, [...finalLeftChildren, ...finalRightChildren]);
|
||||
};
|
||||
|
||||
const matchNodes = (schema, oldChildren, newChildren) => {
|
||||
const matchNodes = (_schema, oldChildren, newChildren) => {
|
||||
const matches = [];
|
||||
for (
|
||||
let oldStartIndex = 0;
|
||||
|
|
@ -155,7 +155,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
!isTextNode(rightOldNode) && matchNodeType(rightOldNode, rightNewNode);
|
||||
if (Array.isArray(leftOldNode) && Array.isArray(leftNewNode)) {
|
||||
finalLeftChildren.push(
|
||||
...patchTextNodes(schema, leftOldNode, leftNewNode),
|
||||
...patchTextNodes(schema, leftOldNode, leftNewNode)
|
||||
);
|
||||
left += 1;
|
||||
continue;
|
||||
|
|
@ -165,7 +165,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
const equalityLeft = computeChildEqualityFactor(leftOldNode, leftNewNode);
|
||||
const equalityRight = computeChildEqualityFactor(
|
||||
rightOldNode,
|
||||
rightNewNode,
|
||||
rightNewNode
|
||||
);
|
||||
if (equalityLeft < equalityRight) {
|
||||
updateLeft = false;
|
||||
|
|
@ -175,21 +175,21 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
}
|
||||
if (updateLeft) {
|
||||
finalLeftChildren.push(
|
||||
patchDocumentNode(schema, leftOldNode, leftNewNode),
|
||||
patchDocumentNode(schema, leftOldNode, leftNewNode)
|
||||
);
|
||||
left += 1;
|
||||
} else if (updateRight) {
|
||||
finalRightChildren.unshift(
|
||||
patchDocumentNode(schema, rightOldNode, rightNewNode),
|
||||
patchDocumentNode(schema, rightOldNode, rightNewNode)
|
||||
);
|
||||
right += 1;
|
||||
} else {
|
||||
// Delete and insert
|
||||
finalLeftChildren.push(
|
||||
createDiffNode(schema, leftOldNode, DiffType.Deleted),
|
||||
createDiffNode(schema, leftOldNode, DiffType.Deleted)
|
||||
);
|
||||
finalLeftChildren.push(
|
||||
createDiffNode(schema, leftNewNode, DiffType.Inserted),
|
||||
createDiffNode(schema, leftNewNode, DiffType.Inserted)
|
||||
);
|
||||
left += 1;
|
||||
}
|
||||
|
|
@ -202,7 +202,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
...oldChildren
|
||||
.slice(left, left + deleteNodeLen)
|
||||
.flat()
|
||||
.map((node) => createDiffNode(schema, node, DiffType.Deleted)),
|
||||
.map((node) => createDiffNode(schema, node, DiffType.Deleted))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +211,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
...newChildren
|
||||
.slice(left, left + insertNodeLen)
|
||||
.flat()
|
||||
.map((node) => createDiffNode(schema, node, DiffType.Inserted)),
|
||||
.map((node) => createDiffNode(schema, node, DiffType.Inserted))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -223,8 +223,8 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
|
|||
const dmp = new diff_match_patch();
|
||||
|
||||
// Concatenate the text from the text nodes
|
||||
const oldText = oldNode.map((n) => getNodeText(n)).join('');
|
||||
const newText = newNode.map((n) => getNodeText(n)).join('');
|
||||
const oldText = oldNode.map((n) => getNodeText(n)).join("");
|
||||
const newText = newNode.map((n) => getNodeText(n)).join("");
|
||||
|
||||
// Tokenize the text into sentences
|
||||
const oldSentences = tokenizeSentences(oldText);
|
||||
|
|
@ -233,7 +233,7 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
|
|||
// Map sentences to unique characters
|
||||
const { chars1, chars2, lineArray } = sentencesToChars(
|
||||
oldSentences,
|
||||
newSentences,
|
||||
newSentences
|
||||
);
|
||||
|
||||
// Perform the diff
|
||||
|
|
@ -242,7 +242,7 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
|
|||
// Convert back to sentences
|
||||
diffs = diffs.map(([type, text]) => {
|
||||
const sentences = text
|
||||
.split('')
|
||||
.split("")
|
||||
.map((char) => lineArray[char.charCodeAt(0)]);
|
||||
return [type, sentences];
|
||||
});
|
||||
|
|
@ -253,7 +253,7 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
|
|||
const node = createTextNode(
|
||||
schema,
|
||||
sentence,
|
||||
type !== DiffType.Unchanged ? [createDiffMark(schema, type)] : [],
|
||||
type !== DiffType.Unchanged ? [createDiffMark(schema, type)] : []
|
||||
);
|
||||
return node;
|
||||
});
|
||||
|
|
@ -284,7 +284,7 @@ const sentencesToChars = (oldSentences, newSentences) => {
|
|||
lineStart++;
|
||||
return String.fromCharCode(lineHash[line]);
|
||||
})
|
||||
.join('');
|
||||
.join("");
|
||||
|
||||
const chars2 = newSentences
|
||||
.map((sentence) => {
|
||||
|
|
@ -297,17 +297,17 @@ const sentencesToChars = (oldSentences, newSentences) => {
|
|||
lineStart++;
|
||||
return String.fromCharCode(lineHash[line]);
|
||||
})
|
||||
.join('');
|
||||
.join("");
|
||||
|
||||
return { chars1, chars2, lineArray };
|
||||
};
|
||||
|
||||
export const computeChildEqualityFactor = (node1, node2) => {
|
||||
export const computeChildEqualityFactor = (_node1, _node2) => {
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const assertNodeTypeEqual = (node1, node2) => {
|
||||
if (getNodeProperty(node1, 'type') !== getNodeProperty(node2, 'type')) {
|
||||
if (getNodeProperty(node1, "type") !== getNodeProperty(node2, "type")) {
|
||||
throw new Error(`node type not equal: ${node1.type} !== ${node2.type}`);
|
||||
}
|
||||
};
|
||||
|
|
@ -329,14 +329,14 @@ export const isNodeEqual = (node1, node2) => {
|
|||
);
|
||||
}
|
||||
|
||||
const type1 = getNodeProperty(node1, 'type');
|
||||
const type2 = getNodeProperty(node2, 'type');
|
||||
const type1 = getNodeProperty(node1, "type");
|
||||
const type2 = getNodeProperty(node2, "type");
|
||||
if (type1 !== type2) {
|
||||
return false;
|
||||
}
|
||||
if (isTextNode(node1)) {
|
||||
const text1 = getNodeProperty(node1, 'text');
|
||||
const text2 = getNodeProperty(node2, 'text');
|
||||
const text1 = getNodeProperty(node1, "text");
|
||||
const text2 = getNodeProperty(node2, "text");
|
||||
if (text1 !== text2) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -396,7 +396,7 @@ export const normalizeNodeContent = (node) => {
|
|||
};
|
||||
|
||||
export const getNodeProperty = (node, property) => {
|
||||
if (property === 'type') {
|
||||
if (property === "type") {
|
||||
return node.type?.name;
|
||||
}
|
||||
return node[property];
|
||||
|
|
@ -413,7 +413,7 @@ export const getNodeChildren = (node) => node.content?.content ?? [];
|
|||
|
||||
export const getNodeText = (node) => node.text;
|
||||
|
||||
export const isTextNode = (node) => node.type?.name === 'text';
|
||||
export const isTextNode = (node) => node.type?.name === "text";
|
||||
|
||||
export const matchNodeType = (node1, node2) =>
|
||||
node1.type?.name === node2.type?.name ||
|
||||
|
|
@ -421,25 +421,25 @@ export const matchNodeType = (node1, node2) =>
|
|||
|
||||
export const createNewNode = (oldNode, children) => {
|
||||
if (!oldNode.type) {
|
||||
throw new Error('oldNode.type is undefined');
|
||||
throw new Error("oldNode.type is undefined");
|
||||
}
|
||||
return new Node(
|
||||
oldNode.type,
|
||||
oldNode.attrs,
|
||||
Fragment.fromArray(children),
|
||||
oldNode.marks,
|
||||
oldNode.marks
|
||||
);
|
||||
};
|
||||
|
||||
export const createDiffNode = (schema, node, type) => {
|
||||
return mapDocumentNode(node, (node) => {
|
||||
if (isTextNode(node)) {
|
||||
return createTextNode(schema, getNodeText(node), [
|
||||
...(node.marks || []),
|
||||
return mapDocumentNode(node, (currentNode) => {
|
||||
if (isTextNode(currentNode)) {
|
||||
return createTextNode(schema, getNodeText(currentNode), [
|
||||
...(currentNode.marks || []),
|
||||
createDiffMark(schema, type),
|
||||
]);
|
||||
}
|
||||
return node;
|
||||
return currentNode;
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -447,21 +447,21 @@ function mapDocumentNode(node, mapper) {
|
|||
const copy = node.copy(
|
||||
Fragment.from(
|
||||
node.content.content
|
||||
.map((node) => mapDocumentNode(node, mapper))
|
||||
.filter((n) => n),
|
||||
),
|
||||
.map((currentNode) => mapDocumentNode(currentNode, mapper))
|
||||
.filter((n) => n)
|
||||
)
|
||||
);
|
||||
return mapper(copy) || copy;
|
||||
}
|
||||
|
||||
export const createDiffMark = (schema, type) => {
|
||||
if (type === DiffType.Inserted) {
|
||||
return schema.mark('diffMark', { type });
|
||||
return schema.mark("diffMark", { type });
|
||||
}
|
||||
if (type === DiffType.Deleted) {
|
||||
return schema.mark('diffMark', { type });
|
||||
return schema.mark("diffMark", { type });
|
||||
}
|
||||
throw new Error('type is not valid');
|
||||
throw new Error("type is not valid");
|
||||
};
|
||||
|
||||
export const createTextNode = (schema, content, marks = []) => {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { defaultMarkdownSerializer } from 'prosemirror-markdown';
|
||||
import { DOMParser, type Node } from 'prosemirror-model';
|
||||
import { Decoration, DecorationSet, type EditorView } from 'prosemirror-view';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { defaultMarkdownSerializer } from "prosemirror-markdown";
|
||||
import { DOMParser, type Node } from "prosemirror-model";
|
||||
import { Decoration, DecorationSet, type EditorView } from "prosemirror-view";
|
||||
import { renderToString } from "react-dom/server";
|
||||
|
||||
import { Response } from '@/components/elements/response';
|
||||
import { Response } from "@/components/elements/response";
|
||||
|
||||
import { documentSchema } from './config';
|
||||
import { createSuggestionWidget, type UISuggestion } from './suggestions';
|
||||
import { documentSchema } from "./config";
|
||||
import { createSuggestionWidget, type UISuggestion } from "./suggestions";
|
||||
|
||||
export const buildDocumentFromContent = (content: string) => {
|
||||
const parser = DOMParser.fromSchema(documentSchema);
|
||||
const stringFromMarkdown = renderToString(<Response>{content}</Response>);
|
||||
const tempContainer = document.createElement('div');
|
||||
const tempContainer = document.createElement("div");
|
||||
tempContainer.innerHTML = stringFromMarkdown;
|
||||
return parser.parse(tempContainer);
|
||||
};
|
||||
|
|
@ -23,10 +23,10 @@ export const buildContentFromDocument = (document: Node) => {
|
|||
};
|
||||
|
||||
export const createDecorations = (
|
||||
suggestions: Array<UISuggestion>,
|
||||
view: EditorView,
|
||||
suggestions: UISuggestion[],
|
||||
view: EditorView
|
||||
) => {
|
||||
const decorations: Array<Decoration> = [];
|
||||
const decorations: Decoration[] = [];
|
||||
|
||||
for (const suggestion of suggestions) {
|
||||
decorations.push(
|
||||
|
|
@ -34,27 +34,27 @@ export const createDecorations = (
|
|||
suggestion.selectionStart,
|
||||
suggestion.selectionEnd,
|
||||
{
|
||||
class: 'suggestion-highlight',
|
||||
class: "suggestion-highlight",
|
||||
},
|
||||
{
|
||||
suggestionId: suggestion.id,
|
||||
type: 'highlight',
|
||||
},
|
||||
),
|
||||
type: "highlight",
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
decorations.push(
|
||||
Decoration.widget(
|
||||
suggestion.selectionStart,
|
||||
(view) => {
|
||||
const { dom } = createSuggestionWidget(suggestion, view);
|
||||
(currentView) => {
|
||||
const { dom } = createSuggestionWidget(suggestion, currentView);
|
||||
return dom;
|
||||
},
|
||||
{
|
||||
suggestionId: suggestion.id,
|
||||
type: 'widget',
|
||||
},
|
||||
),
|
||||
type: "widget",
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { createRoot } from 'react-dom/client';
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
// biome-ignore lint/complexity/noStaticOnlyClass: "Needs to be static"
|
||||
export class ReactRenderer {
|
||||
static render(component: React.ReactElement, dom: HTMLElement) {
|
||||
const root = createRoot(dom);
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
import type { Node } from 'prosemirror-model';
|
||||
import { Plugin, PluginKey } from 'prosemirror-state';
|
||||
import type { Node } from "prosemirror-model";
|
||||
import { Plugin, PluginKey } from "prosemirror-state";
|
||||
import {
|
||||
type Decoration,
|
||||
DecorationSet,
|
||||
type EditorView,
|
||||
} from 'prosemirror-view';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import { Suggestion as PreviewSuggestion } from '@/components/suggestion';
|
||||
import type { Suggestion } from '@/lib/db/schema';
|
||||
import type { ArtifactKind } from '@/components/artifact';
|
||||
} from "prosemirror-view";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import type { ArtifactKind } from "@/components/artifact";
|
||||
import { Suggestion as PreviewSuggestion } from "@/components/suggestion";
|
||||
import type { Suggestion } from "@/lib/db/schema";
|
||||
|
||||
export interface UISuggestion extends Suggestion {
|
||||
selectionStart: number;
|
||||
selectionEnd: number;
|
||||
}
|
||||
|
||||
interface Position {
|
||||
type Position = {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
};
|
||||
|
||||
function findPositionsInDoc(doc: Node, searchText: string): Position | null {
|
||||
let positions: { start: number; end: number } | null = null;
|
||||
|
|
@ -46,8 +45,8 @@ function findPositionsInDoc(doc: Node, searchText: string): Position | null {
|
|||
|
||||
export function projectWithPositions(
|
||||
doc: Node,
|
||||
suggestions: Array<Suggestion>,
|
||||
): Array<UISuggestion> {
|
||||
suggestions: Suggestion[]
|
||||
): UISuggestion[] {
|
||||
return suggestions.map((suggestion) => {
|
||||
const positions = findPositionsInDoc(doc, suggestion.originalText);
|
||||
|
||||
|
|
@ -70,12 +69,12 @@ export function projectWithPositions(
|
|||
export function createSuggestionWidget(
|
||||
suggestion: UISuggestion,
|
||||
view: EditorView,
|
||||
artifactKind: ArtifactKind = 'text',
|
||||
artifactKind: ArtifactKind = "text"
|
||||
): { dom: HTMLElement; destroy: () => void } {
|
||||
const dom = document.createElement('span');
|
||||
const dom = document.createElement("span");
|
||||
const root = createRoot(dom);
|
||||
|
||||
dom.addEventListener('mousedown', (event) => {
|
||||
dom.addEventListener("mousedown", (event) => {
|
||||
event.preventDefault();
|
||||
view.dom.blur();
|
||||
});
|
||||
|
|
@ -92,7 +91,7 @@ export function createSuggestionWidget(
|
|||
state.doc,
|
||||
currentDecorations.find().filter((decoration: Decoration) => {
|
||||
return decoration.spec.suggestionId !== suggestion.id;
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
decorationTransaction.setMeta(suggestionsPluginKey, {
|
||||
|
|
@ -105,20 +104,20 @@ export function createSuggestionWidget(
|
|||
const textTransaction = view.state.tr.replaceWith(
|
||||
suggestion.selectionStart,
|
||||
suggestion.selectionEnd,
|
||||
state.schema.text(suggestion.suggestedText),
|
||||
state.schema.text(suggestion.suggestedText)
|
||||
);
|
||||
|
||||
textTransaction.setMeta('no-debounce', true);
|
||||
textTransaction.setMeta("no-debounce", true);
|
||||
|
||||
dispatch(textTransaction);
|
||||
};
|
||||
|
||||
root.render(
|
||||
<PreviewSuggestion
|
||||
suggestion={suggestion}
|
||||
onApply={onApply}
|
||||
artifactKind={artifactKind}
|
||||
/>,
|
||||
onApply={onApply}
|
||||
suggestion={suggestion}
|
||||
/>
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -132,7 +131,7 @@ export function createSuggestionWidget(
|
|||
};
|
||||
}
|
||||
|
||||
export const suggestionsPluginKey = new PluginKey('suggestions');
|
||||
export const suggestionsPluginKey = new PluginKey("suggestions");
|
||||
export const suggestionsPlugin = new Plugin({
|
||||
key: suggestionsPluginKey,
|
||||
state: {
|
||||
|
|
@ -141,7 +140,9 @@ export const suggestionsPlugin = new Plugin({
|
|||
},
|
||||
apply(tr, state) {
|
||||
const newDecorations = tr.getMeta(suggestionsPluginKey);
|
||||
if (newDecorations) return newDecorations;
|
||||
if (newDecorations) {
|
||||
return newDecorations;
|
||||
}
|
||||
|
||||
return {
|
||||
decorations: state.decorations.map(tr.mapping, tr.doc),
|
||||
|
|
|
|||
136
lib/errors.ts
136
lib/errors.ts
|
|
@ -1,49 +1,49 @@
|
|||
export type ErrorType =
|
||||
| 'bad_request'
|
||||
| 'unauthorized'
|
||||
| 'forbidden'
|
||||
| 'not_found'
|
||||
| 'rate_limit'
|
||||
| 'offline';
|
||||
| "bad_request"
|
||||
| "unauthorized"
|
||||
| "forbidden"
|
||||
| "not_found"
|
||||
| "rate_limit"
|
||||
| "offline";
|
||||
|
||||
export type Surface =
|
||||
| 'chat'
|
||||
| 'auth'
|
||||
| 'api'
|
||||
| 'stream'
|
||||
| 'database'
|
||||
| 'history'
|
||||
| 'vote'
|
||||
| 'document'
|
||||
| 'suggestions'
|
||||
| 'activate_gateway';
|
||||
| "chat"
|
||||
| "auth"
|
||||
| "api"
|
||||
| "stream"
|
||||
| "database"
|
||||
| "history"
|
||||
| "vote"
|
||||
| "document"
|
||||
| "suggestions"
|
||||
| "activate_gateway";
|
||||
|
||||
export type ErrorCode = `${ErrorType}:${Surface}`;
|
||||
|
||||
export type ErrorVisibility = 'response' | 'log' | 'none';
|
||||
export type ErrorVisibility = "response" | "log" | "none";
|
||||
|
||||
export const visibilityBySurface: Record<Surface, ErrorVisibility> = {
|
||||
database: 'log',
|
||||
chat: 'response',
|
||||
auth: 'response',
|
||||
stream: 'response',
|
||||
api: 'response',
|
||||
history: 'response',
|
||||
vote: 'response',
|
||||
document: 'response',
|
||||
suggestions: 'response',
|
||||
activate_gateway: 'response',
|
||||
database: "log",
|
||||
chat: "response",
|
||||
auth: "response",
|
||||
stream: "response",
|
||||
api: "response",
|
||||
history: "response",
|
||||
vote: "response",
|
||||
document: "response",
|
||||
suggestions: "response",
|
||||
activate_gateway: "response",
|
||||
};
|
||||
|
||||
export class ChatSDKError extends Error {
|
||||
public type: ErrorType;
|
||||
public surface: Surface;
|
||||
public statusCode: number;
|
||||
type: ErrorType;
|
||||
surface: Surface;
|
||||
statusCode: number;
|
||||
|
||||
constructor(errorCode: ErrorCode, cause?: string) {
|
||||
super();
|
||||
|
||||
const [type, surface] = errorCode.split(':');
|
||||
const [type, surface] = errorCode.split(":");
|
||||
|
||||
this.type = type as ErrorType;
|
||||
this.cause = cause;
|
||||
|
|
@ -52,13 +52,13 @@ export class ChatSDKError extends Error {
|
|||
this.statusCode = getStatusCodeByType(this.type);
|
||||
}
|
||||
|
||||
public toResponse() {
|
||||
toResponse() {
|
||||
const code: ErrorCode = `${this.type}:${this.surface}`;
|
||||
const visibility = visibilityBySurface[this.surface];
|
||||
|
||||
const { message, cause, statusCode } = this;
|
||||
|
||||
if (visibility === 'log') {
|
||||
if (visibility === "log") {
|
||||
console.error({
|
||||
code,
|
||||
message,
|
||||
|
|
@ -66,8 +66,8 @@ export class ChatSDKError extends Error {
|
|||
});
|
||||
|
||||
return Response.json(
|
||||
{ code: '', message: 'Something went wrong. Please try again later.' },
|
||||
{ status: statusCode },
|
||||
{ code: "", message: "Something went wrong. Please try again later." },
|
||||
{ status: statusCode }
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -76,60 +76,60 @@ export class ChatSDKError extends Error {
|
|||
}
|
||||
|
||||
export function getMessageByErrorCode(errorCode: ErrorCode): string {
|
||||
if (errorCode.includes('database')) {
|
||||
return 'An error occurred while executing a database query.';
|
||||
if (errorCode.includes("database")) {
|
||||
return "An error occurred while executing a database query.";
|
||||
}
|
||||
|
||||
switch (errorCode) {
|
||||
case 'bad_request:api':
|
||||
case "bad_request:api":
|
||||
return "The request couldn't be processed. Please check your input and try again.";
|
||||
|
||||
case 'bad_request:activate_gateway':
|
||||
return 'AI Gateway requires a valid credit card on file to service requests. Please visit https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dadd-credit-card to add a card and unlock your free credits.';
|
||||
case "bad_request:activate_gateway":
|
||||
return "AI Gateway requires a valid credit card on file to service requests. Please visit https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dadd-credit-card to add a card and unlock your free credits.";
|
||||
|
||||
case 'unauthorized:auth':
|
||||
return 'You need to sign in before continuing.';
|
||||
case 'forbidden:auth':
|
||||
return 'Your account does not have access to this feature.';
|
||||
case "unauthorized:auth":
|
||||
return "You need to sign in before continuing.";
|
||||
case "forbidden:auth":
|
||||
return "Your account does not have access to this feature.";
|
||||
|
||||
case 'rate_limit:chat':
|
||||
return 'You have exceeded your maximum number of messages for the day. Please try again later.';
|
||||
case 'not_found:chat':
|
||||
return 'The requested chat was not found. Please check the chat ID and try again.';
|
||||
case 'forbidden:chat':
|
||||
return 'This chat belongs to another user. Please check the chat ID and try again.';
|
||||
case 'unauthorized:chat':
|
||||
return 'You need to sign in to view this chat. Please sign in and try again.';
|
||||
case 'offline:chat':
|
||||
case "rate_limit:chat":
|
||||
return "You have exceeded your maximum number of messages for the day. Please try again later.";
|
||||
case "not_found:chat":
|
||||
return "The requested chat was not found. Please check the chat ID and try again.";
|
||||
case "forbidden:chat":
|
||||
return "This chat belongs to another user. Please check the chat ID and try again.";
|
||||
case "unauthorized:chat":
|
||||
return "You need to sign in to view this chat. Please sign in and try again.";
|
||||
case "offline:chat":
|
||||
return "We're having trouble sending your message. Please check your internet connection and try again.";
|
||||
|
||||
case 'not_found:document':
|
||||
return 'The requested document was not found. Please check the document ID and try again.';
|
||||
case 'forbidden:document':
|
||||
return 'This document belongs to another user. Please check the document ID and try again.';
|
||||
case 'unauthorized:document':
|
||||
return 'You need to sign in to view this document. Please sign in and try again.';
|
||||
case 'bad_request:document':
|
||||
return 'The request to create or update the document was invalid. Please check your input and try again.';
|
||||
case "not_found:document":
|
||||
return "The requested document was not found. Please check the document ID and try again.";
|
||||
case "forbidden:document":
|
||||
return "This document belongs to another user. Please check the document ID and try again.";
|
||||
case "unauthorized:document":
|
||||
return "You need to sign in to view this document. Please sign in and try again.";
|
||||
case "bad_request:document":
|
||||
return "The request to create or update the document was invalid. Please check your input and try again.";
|
||||
|
||||
default:
|
||||
return 'Something went wrong. Please try again later.';
|
||||
return "Something went wrong. Please try again later.";
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusCodeByType(type: ErrorType) {
|
||||
switch (type) {
|
||||
case 'bad_request':
|
||||
case "bad_request":
|
||||
return 400;
|
||||
case 'unauthorized':
|
||||
case "unauthorized":
|
||||
return 401;
|
||||
case 'forbidden':
|
||||
case "forbidden":
|
||||
return 403;
|
||||
case 'not_found':
|
||||
case "not_found":
|
||||
return 404;
|
||||
case 'rate_limit':
|
||||
case "rate_limit":
|
||||
return 429;
|
||||
case 'offline':
|
||||
case "offline":
|
||||
return 503;
|
||||
default:
|
||||
return 500;
|
||||
|
|
|
|||
25
lib/types.ts
25
lib/types.ts
|
|
@ -1,15 +1,14 @@
|
|||
import { z } from 'zod';
|
||||
import type { getWeather } from './ai/tools/get-weather';
|
||||
import type { createDocument } from './ai/tools/create-document';
|
||||
import type { updateDocument } from './ai/tools/update-document';
|
||||
import type { requestSuggestions } from './ai/tools/request-suggestions';
|
||||
import type { InferUITool, UIMessage } from 'ai';
|
||||
import type { AppUsage } from './usage';
|
||||
import type { InferUITool, UIMessage } from "ai";
|
||||
import { z } from "zod";
|
||||
import type { ArtifactKind } from "@/components/artifact";
|
||||
import type { createDocument } from "./ai/tools/create-document";
|
||||
import type { getWeather } from "./ai/tools/get-weather";
|
||||
import type { requestSuggestions } from "./ai/tools/request-suggestions";
|
||||
import type { updateDocument } from "./ai/tools/update-document";
|
||||
import type { Suggestion } from "./db/schema";
|
||||
import type { AppUsage } from "./usage";
|
||||
|
||||
import type { ArtifactKind } from '@/components/artifact';
|
||||
import type { Suggestion } from './db/schema';
|
||||
|
||||
export type DataPart = { type: 'append-message'; message: string };
|
||||
export type DataPart = { type: "append-message"; message: string };
|
||||
|
||||
export const messageMetadataSchema = z.object({
|
||||
createdAt: z.string(),
|
||||
|
|
@ -52,8 +51,8 @@ export type ChatMessage = UIMessage<
|
|||
ChatTools
|
||||
>;
|
||||
|
||||
export interface Attachment {
|
||||
export type Attachment = {
|
||||
name: string;
|
||||
url: string;
|
||||
contentType: string;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { LanguageModelUsage } from 'ai';
|
||||
import type { UsageData } from 'tokenlens/helpers';
|
||||
import type { LanguageModelUsage } from "ai";
|
||||
import type { UsageData } from "tokenlens/helpers";
|
||||
|
||||
// Server-merged usage: base usage + TokenLens summary + optional modelId
|
||||
export type AppUsage = LanguageModelUsage & UsageData & { modelId?: string };
|
||||
|
|
|
|||
14
lib/utils.ts
14
lib/utils.ts
|
|
@ -5,11 +5,11 @@ import type {
|
|||
UIMessagePart,
|
||||
} from 'ai';
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { formatISO } from 'date-fns';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { DBMessage, Document } from '@/lib/db/schema';
|
||||
import { ChatSDKError, type ErrorCode } from './errors';
|
||||
import type { ChatMessage, ChatTools, CustomUIDataTypes } from './types';
|
||||
import { formatISO } from 'date-fns';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
|
|
@ -66,17 +66,17 @@ export function generateUUID(): string {
|
|||
type ResponseMessageWithoutId = CoreToolMessage | CoreAssistantMessage;
|
||||
type ResponseMessage = ResponseMessageWithoutId & { id: string };
|
||||
|
||||
export function getMostRecentUserMessage(messages: Array<UIMessage>) {
|
||||
export function getMostRecentUserMessage(messages: UIMessage[]) {
|
||||
const userMessages = messages.filter((message) => message.role === 'user');
|
||||
return userMessages.at(-1);
|
||||
}
|
||||
|
||||
export function getDocumentTimestampByIndex(
|
||||
documents: Array<Document>,
|
||||
documents: Document[],
|
||||
index: number,
|
||||
) {
|
||||
if (!documents) return new Date();
|
||||
if (index > documents.length) return new Date();
|
||||
if (!documents) { return new Date(); }
|
||||
if (index > documents.length) { return new Date(); }
|
||||
|
||||
return documents[index].createdAt;
|
||||
}
|
||||
|
|
@ -84,11 +84,11 @@ export function getDocumentTimestampByIndex(
|
|||
export function getTrailingMessageId({
|
||||
messages,
|
||||
}: {
|
||||
messages: Array<ResponseMessage>;
|
||||
messages: ResponseMessage[];
|
||||
}): string | null {
|
||||
const trailingMessage = messages.at(-1);
|
||||
|
||||
if (!trailingMessage) return null;
|
||||
if (!trailingMessage) { return null; }
|
||||
|
||||
return trailingMessage.id;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue