@firstlovecenter/ai-chat 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/drizzle/index.cjs +199 -0
- package/dist/drizzle/index.cjs.map +1 -0
- package/dist/drizzle/index.d.cts +361 -0
- package/dist/drizzle/index.d.ts +361 -0
- package/dist/drizzle/index.js +194 -0
- package/dist/drizzle/index.js.map +1 -0
- package/dist/prisma/index.cjs +163 -0
- package/dist/prisma/index.cjs.map +1 -0
- package/dist/prisma/index.d.cts +163 -0
- package/dist/prisma/index.d.ts +163 -0
- package/dist/prisma/index.js +160 -0
- package/dist/prisma/index.js.map +1 -0
- package/dist/server/index.cjs +1465 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +487 -0
- package/dist/server/index.d.ts +487 -0
- package/dist/server/index.js +1450 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types-DNwFvL-C.d.cts +268 -0
- package/dist/types-DNwFvL-C.d.ts +268 -0
- package/dist/ui/index.cjs +1388 -0
- package/dist/ui/index.cjs.map +1 -0
- package/dist/ui/index.d.cts +89 -0
- package/dist/ui/index.d.ts +89 -0
- package/dist/ui/index.js +1365 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +112 -0
- package/prisma/chat-models.prisma +61 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import * as drizzle_orm_mysql_core from 'drizzle-orm/mysql-core';
|
|
2
|
+
import { MySql2Database } from 'drizzle-orm/mysql2';
|
|
3
|
+
import { c as PersistencePort } from '../types-DNwFvL-C.js';
|
|
4
|
+
import 'google-auth-library';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Global AI configuration. Singleton row enforced by `id=1` default + PK.
|
|
8
|
+
* `tool_provider`, `gcp_location`, and `chat_interface` are open VARCHARs;
|
|
9
|
+
* the runtime layer validates against the host's registries before write.
|
|
10
|
+
*/
|
|
11
|
+
declare const aiSettings: drizzle_orm_mysql_core.MySqlTableWithColumns<{
|
|
12
|
+
name: "ai_settings";
|
|
13
|
+
schema: undefined;
|
|
14
|
+
columns: {
|
|
15
|
+
id: drizzle_orm_mysql_core.MySqlColumn<{
|
|
16
|
+
name: "id";
|
|
17
|
+
tableName: "ai_settings";
|
|
18
|
+
dataType: "number";
|
|
19
|
+
columnType: "MySqlTinyInt";
|
|
20
|
+
data: number;
|
|
21
|
+
driverParam: string | number;
|
|
22
|
+
notNull: true;
|
|
23
|
+
hasDefault: true;
|
|
24
|
+
isPrimaryKey: true;
|
|
25
|
+
isAutoincrement: false;
|
|
26
|
+
hasRuntimeDefault: false;
|
|
27
|
+
enumValues: undefined;
|
|
28
|
+
baseColumn: never;
|
|
29
|
+
identity: undefined;
|
|
30
|
+
generated: undefined;
|
|
31
|
+
}, object>;
|
|
32
|
+
toolProvider: drizzle_orm_mysql_core.MySqlColumn<{
|
|
33
|
+
name: "tool_provider";
|
|
34
|
+
tableName: "ai_settings";
|
|
35
|
+
dataType: "string";
|
|
36
|
+
columnType: "MySqlVarChar";
|
|
37
|
+
data: string;
|
|
38
|
+
driverParam: string | number;
|
|
39
|
+
notNull: true;
|
|
40
|
+
hasDefault: true;
|
|
41
|
+
isPrimaryKey: false;
|
|
42
|
+
isAutoincrement: false;
|
|
43
|
+
hasRuntimeDefault: false;
|
|
44
|
+
enumValues: [string, ...string[]];
|
|
45
|
+
baseColumn: never;
|
|
46
|
+
identity: undefined;
|
|
47
|
+
generated: undefined;
|
|
48
|
+
}, object>;
|
|
49
|
+
gcpLocation: drizzle_orm_mysql_core.MySqlColumn<{
|
|
50
|
+
name: "gcp_location";
|
|
51
|
+
tableName: "ai_settings";
|
|
52
|
+
dataType: "string";
|
|
53
|
+
columnType: "MySqlVarChar";
|
|
54
|
+
data: string;
|
|
55
|
+
driverParam: string | number;
|
|
56
|
+
notNull: true;
|
|
57
|
+
hasDefault: true;
|
|
58
|
+
isPrimaryKey: false;
|
|
59
|
+
isAutoincrement: false;
|
|
60
|
+
hasRuntimeDefault: false;
|
|
61
|
+
enumValues: [string, ...string[]];
|
|
62
|
+
baseColumn: never;
|
|
63
|
+
identity: undefined;
|
|
64
|
+
generated: undefined;
|
|
65
|
+
}, object>;
|
|
66
|
+
chatInterface: drizzle_orm_mysql_core.MySqlColumn<{
|
|
67
|
+
name: "chat_interface";
|
|
68
|
+
tableName: "ai_settings";
|
|
69
|
+
dataType: "string";
|
|
70
|
+
columnType: "MySqlVarChar";
|
|
71
|
+
data: string;
|
|
72
|
+
driverParam: string | number;
|
|
73
|
+
notNull: true;
|
|
74
|
+
hasDefault: true;
|
|
75
|
+
isPrimaryKey: false;
|
|
76
|
+
isAutoincrement: false;
|
|
77
|
+
hasRuntimeDefault: false;
|
|
78
|
+
enumValues: [string, ...string[]];
|
|
79
|
+
baseColumn: never;
|
|
80
|
+
identity: undefined;
|
|
81
|
+
generated: undefined;
|
|
82
|
+
}, object>;
|
|
83
|
+
updatedAt: drizzle_orm_mysql_core.MySqlColumn<{
|
|
84
|
+
name: "updated_at";
|
|
85
|
+
tableName: "ai_settings";
|
|
86
|
+
dataType: "string";
|
|
87
|
+
columnType: "MySqlDateTimeString";
|
|
88
|
+
data: string;
|
|
89
|
+
driverParam: string | number;
|
|
90
|
+
notNull: true;
|
|
91
|
+
hasDefault: true;
|
|
92
|
+
isPrimaryKey: false;
|
|
93
|
+
isAutoincrement: false;
|
|
94
|
+
hasRuntimeDefault: false;
|
|
95
|
+
enumValues: undefined;
|
|
96
|
+
baseColumn: never;
|
|
97
|
+
identity: undefined;
|
|
98
|
+
generated: undefined;
|
|
99
|
+
}, object>;
|
|
100
|
+
updatedByUserId: drizzle_orm_mysql_core.MySqlColumn<{
|
|
101
|
+
name: string;
|
|
102
|
+
tableName: "ai_settings";
|
|
103
|
+
dataType: "number";
|
|
104
|
+
columnType: "MySqlBigInt53";
|
|
105
|
+
data: number;
|
|
106
|
+
driverParam: string | number;
|
|
107
|
+
notNull: false;
|
|
108
|
+
hasDefault: false;
|
|
109
|
+
isPrimaryKey: false;
|
|
110
|
+
isAutoincrement: false;
|
|
111
|
+
hasRuntimeDefault: false;
|
|
112
|
+
enumValues: undefined;
|
|
113
|
+
baseColumn: never;
|
|
114
|
+
identity: undefined;
|
|
115
|
+
generated: undefined;
|
|
116
|
+
}, object>;
|
|
117
|
+
};
|
|
118
|
+
dialect: "mysql";
|
|
119
|
+
}>;
|
|
120
|
+
declare const chatSessions: drizzle_orm_mysql_core.MySqlTableWithColumns<{
|
|
121
|
+
name: "chat_sessions";
|
|
122
|
+
schema: undefined;
|
|
123
|
+
columns: {
|
|
124
|
+
id: drizzle_orm_mysql_core.MySqlColumn<{
|
|
125
|
+
name: "id";
|
|
126
|
+
tableName: "chat_sessions";
|
|
127
|
+
dataType: "number";
|
|
128
|
+
columnType: "MySqlBigInt53";
|
|
129
|
+
data: number;
|
|
130
|
+
driverParam: string | number;
|
|
131
|
+
notNull: true;
|
|
132
|
+
hasDefault: true;
|
|
133
|
+
isPrimaryKey: true;
|
|
134
|
+
isAutoincrement: true;
|
|
135
|
+
hasRuntimeDefault: false;
|
|
136
|
+
enumValues: undefined;
|
|
137
|
+
baseColumn: never;
|
|
138
|
+
identity: undefined;
|
|
139
|
+
generated: undefined;
|
|
140
|
+
}, object>;
|
|
141
|
+
userId: drizzle_orm_mysql_core.MySqlColumn<{
|
|
142
|
+
name: string;
|
|
143
|
+
tableName: "chat_sessions";
|
|
144
|
+
dataType: "number";
|
|
145
|
+
columnType: "MySqlBigInt53";
|
|
146
|
+
data: number;
|
|
147
|
+
driverParam: string | number;
|
|
148
|
+
notNull: true;
|
|
149
|
+
hasDefault: false;
|
|
150
|
+
isPrimaryKey: false;
|
|
151
|
+
isAutoincrement: false;
|
|
152
|
+
hasRuntimeDefault: false;
|
|
153
|
+
enumValues: undefined;
|
|
154
|
+
baseColumn: never;
|
|
155
|
+
identity: undefined;
|
|
156
|
+
generated: undefined;
|
|
157
|
+
}, object>;
|
|
158
|
+
title: drizzle_orm_mysql_core.MySqlColumn<{
|
|
159
|
+
name: "title";
|
|
160
|
+
tableName: "chat_sessions";
|
|
161
|
+
dataType: "string";
|
|
162
|
+
columnType: "MySqlVarChar";
|
|
163
|
+
data: string;
|
|
164
|
+
driverParam: string | number;
|
|
165
|
+
notNull: true;
|
|
166
|
+
hasDefault: false;
|
|
167
|
+
isPrimaryKey: false;
|
|
168
|
+
isAutoincrement: false;
|
|
169
|
+
hasRuntimeDefault: false;
|
|
170
|
+
enumValues: [string, ...string[]];
|
|
171
|
+
baseColumn: never;
|
|
172
|
+
identity: undefined;
|
|
173
|
+
generated: undefined;
|
|
174
|
+
}, object>;
|
|
175
|
+
createdAt: drizzle_orm_mysql_core.MySqlColumn<{
|
|
176
|
+
name: "created_at";
|
|
177
|
+
tableName: "chat_sessions";
|
|
178
|
+
dataType: "string";
|
|
179
|
+
columnType: "MySqlDateTimeString";
|
|
180
|
+
data: string;
|
|
181
|
+
driverParam: string | number;
|
|
182
|
+
notNull: true;
|
|
183
|
+
hasDefault: true;
|
|
184
|
+
isPrimaryKey: false;
|
|
185
|
+
isAutoincrement: false;
|
|
186
|
+
hasRuntimeDefault: false;
|
|
187
|
+
enumValues: undefined;
|
|
188
|
+
baseColumn: never;
|
|
189
|
+
identity: undefined;
|
|
190
|
+
generated: undefined;
|
|
191
|
+
}, object>;
|
|
192
|
+
updatedAt: drizzle_orm_mysql_core.MySqlColumn<{
|
|
193
|
+
name: "updated_at";
|
|
194
|
+
tableName: "chat_sessions";
|
|
195
|
+
dataType: "string";
|
|
196
|
+
columnType: "MySqlDateTimeString";
|
|
197
|
+
data: string;
|
|
198
|
+
driverParam: string | number;
|
|
199
|
+
notNull: true;
|
|
200
|
+
hasDefault: true;
|
|
201
|
+
isPrimaryKey: false;
|
|
202
|
+
isAutoincrement: false;
|
|
203
|
+
hasRuntimeDefault: false;
|
|
204
|
+
enumValues: undefined;
|
|
205
|
+
baseColumn: never;
|
|
206
|
+
identity: undefined;
|
|
207
|
+
generated: undefined;
|
|
208
|
+
}, object>;
|
|
209
|
+
};
|
|
210
|
+
dialect: "mysql";
|
|
211
|
+
}>;
|
|
212
|
+
declare const chatMessages: drizzle_orm_mysql_core.MySqlTableWithColumns<{
|
|
213
|
+
name: "chat_messages";
|
|
214
|
+
schema: undefined;
|
|
215
|
+
columns: {
|
|
216
|
+
id: drizzle_orm_mysql_core.MySqlColumn<{
|
|
217
|
+
name: "id";
|
|
218
|
+
tableName: "chat_messages";
|
|
219
|
+
dataType: "number";
|
|
220
|
+
columnType: "MySqlBigInt53";
|
|
221
|
+
data: number;
|
|
222
|
+
driverParam: string | number;
|
|
223
|
+
notNull: true;
|
|
224
|
+
hasDefault: true;
|
|
225
|
+
isPrimaryKey: true;
|
|
226
|
+
isAutoincrement: true;
|
|
227
|
+
hasRuntimeDefault: false;
|
|
228
|
+
enumValues: undefined;
|
|
229
|
+
baseColumn: never;
|
|
230
|
+
identity: undefined;
|
|
231
|
+
generated: undefined;
|
|
232
|
+
}, object>;
|
|
233
|
+
sessionId: drizzle_orm_mysql_core.MySqlColumn<{
|
|
234
|
+
name: string;
|
|
235
|
+
tableName: "chat_messages";
|
|
236
|
+
dataType: "number";
|
|
237
|
+
columnType: "MySqlBigInt53";
|
|
238
|
+
data: number;
|
|
239
|
+
driverParam: string | number;
|
|
240
|
+
notNull: true;
|
|
241
|
+
hasDefault: false;
|
|
242
|
+
isPrimaryKey: false;
|
|
243
|
+
isAutoincrement: false;
|
|
244
|
+
hasRuntimeDefault: false;
|
|
245
|
+
enumValues: undefined;
|
|
246
|
+
baseColumn: never;
|
|
247
|
+
identity: undefined;
|
|
248
|
+
generated: undefined;
|
|
249
|
+
}, object>;
|
|
250
|
+
role: drizzle_orm_mysql_core.MySqlColumn<{
|
|
251
|
+
name: "role";
|
|
252
|
+
tableName: "chat_messages";
|
|
253
|
+
dataType: "string";
|
|
254
|
+
columnType: "MySqlEnumColumn";
|
|
255
|
+
data: "user" | "assistant";
|
|
256
|
+
driverParam: string;
|
|
257
|
+
notNull: true;
|
|
258
|
+
hasDefault: false;
|
|
259
|
+
isPrimaryKey: false;
|
|
260
|
+
isAutoincrement: false;
|
|
261
|
+
hasRuntimeDefault: false;
|
|
262
|
+
enumValues: ["user", "assistant"];
|
|
263
|
+
baseColumn: never;
|
|
264
|
+
identity: undefined;
|
|
265
|
+
generated: undefined;
|
|
266
|
+
}, object>;
|
|
267
|
+
question: drizzle_orm_mysql_core.MySqlColumn<{
|
|
268
|
+
name: "question";
|
|
269
|
+
tableName: "chat_messages";
|
|
270
|
+
dataType: "string";
|
|
271
|
+
columnType: "MySqlText";
|
|
272
|
+
data: string;
|
|
273
|
+
driverParam: string;
|
|
274
|
+
notNull: false;
|
|
275
|
+
hasDefault: false;
|
|
276
|
+
isPrimaryKey: false;
|
|
277
|
+
isAutoincrement: false;
|
|
278
|
+
hasRuntimeDefault: false;
|
|
279
|
+
enumValues: [string, ...string[]];
|
|
280
|
+
baseColumn: never;
|
|
281
|
+
identity: undefined;
|
|
282
|
+
generated: undefined;
|
|
283
|
+
}, object>;
|
|
284
|
+
blocks: drizzle_orm_mysql_core.MySqlColumn<{
|
|
285
|
+
name: "blocks";
|
|
286
|
+
tableName: "chat_messages";
|
|
287
|
+
dataType: "json";
|
|
288
|
+
columnType: "MySqlJson";
|
|
289
|
+
data: unknown;
|
|
290
|
+
driverParam: string;
|
|
291
|
+
notNull: false;
|
|
292
|
+
hasDefault: false;
|
|
293
|
+
isPrimaryKey: false;
|
|
294
|
+
isAutoincrement: false;
|
|
295
|
+
hasRuntimeDefault: false;
|
|
296
|
+
enumValues: undefined;
|
|
297
|
+
baseColumn: never;
|
|
298
|
+
identity: undefined;
|
|
299
|
+
generated: undefined;
|
|
300
|
+
}, object>;
|
|
301
|
+
prose: drizzle_orm_mysql_core.MySqlColumn<{
|
|
302
|
+
name: "prose";
|
|
303
|
+
tableName: "chat_messages";
|
|
304
|
+
dataType: "json";
|
|
305
|
+
columnType: "MySqlJson";
|
|
306
|
+
data: unknown;
|
|
307
|
+
driverParam: string;
|
|
308
|
+
notNull: false;
|
|
309
|
+
hasDefault: false;
|
|
310
|
+
isPrimaryKey: false;
|
|
311
|
+
isAutoincrement: false;
|
|
312
|
+
hasRuntimeDefault: false;
|
|
313
|
+
enumValues: undefined;
|
|
314
|
+
baseColumn: never;
|
|
315
|
+
identity: undefined;
|
|
316
|
+
generated: undefined;
|
|
317
|
+
}, object>;
|
|
318
|
+
errorJson: drizzle_orm_mysql_core.MySqlColumn<{
|
|
319
|
+
name: "error_json";
|
|
320
|
+
tableName: "chat_messages";
|
|
321
|
+
dataType: "json";
|
|
322
|
+
columnType: "MySqlJson";
|
|
323
|
+
data: unknown;
|
|
324
|
+
driverParam: string;
|
|
325
|
+
notNull: false;
|
|
326
|
+
hasDefault: false;
|
|
327
|
+
isPrimaryKey: false;
|
|
328
|
+
isAutoincrement: false;
|
|
329
|
+
hasRuntimeDefault: false;
|
|
330
|
+
enumValues: undefined;
|
|
331
|
+
baseColumn: never;
|
|
332
|
+
identity: undefined;
|
|
333
|
+
generated: undefined;
|
|
334
|
+
}, object>;
|
|
335
|
+
createdAt: drizzle_orm_mysql_core.MySqlColumn<{
|
|
336
|
+
name: "created_at";
|
|
337
|
+
tableName: "chat_messages";
|
|
338
|
+
dataType: "string";
|
|
339
|
+
columnType: "MySqlDateTimeString";
|
|
340
|
+
data: string;
|
|
341
|
+
driverParam: string | number;
|
|
342
|
+
notNull: true;
|
|
343
|
+
hasDefault: true;
|
|
344
|
+
isPrimaryKey: false;
|
|
345
|
+
isAutoincrement: false;
|
|
346
|
+
hasRuntimeDefault: false;
|
|
347
|
+
enumValues: undefined;
|
|
348
|
+
baseColumn: never;
|
|
349
|
+
identity: undefined;
|
|
350
|
+
generated: undefined;
|
|
351
|
+
}, object>;
|
|
352
|
+
};
|
|
353
|
+
dialect: "mysql";
|
|
354
|
+
}>;
|
|
355
|
+
type ChatSessionRow = typeof chatSessions.$inferSelect;
|
|
356
|
+
type ChatMessageRow = typeof chatMessages.$inferSelect;
|
|
357
|
+
type AiSettingsRow = typeof aiSettings.$inferSelect;
|
|
358
|
+
|
|
359
|
+
declare function createDrizzlePersistence(db: MySql2Database<any>): PersistencePort;
|
|
360
|
+
|
|
361
|
+
export { type AiSettingsRow, type ChatMessageRow, type ChatSessionRow, aiSettings, chatMessages, chatSessions, createDrizzlePersistence };
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { sql, eq, and, desc } from 'drizzle-orm';
|
|
2
|
+
import { mysqlTable, datetime, varchar, tinyint, index, json, text, mysqlEnum, bigint } from 'drizzle-orm/mysql-core';
|
|
3
|
+
|
|
4
|
+
// src/adapters/drizzle/tables.ts
|
|
5
|
+
var bigintPk = () => bigint("id", { mode: "number", unsigned: true }).notNull().primaryKey().autoincrement();
|
|
6
|
+
var bigintFk = (name) => bigint(name, { mode: "number", unsigned: true }).notNull();
|
|
7
|
+
var bigintFkNullable = (name) => bigint(name, { mode: "number", unsigned: true });
|
|
8
|
+
var createdAt = () => datetime("created_at", { mode: "string" }).notNull().default(sql`CURRENT_TIMESTAMP`);
|
|
9
|
+
var aiSettings = mysqlTable("ai_settings", {
|
|
10
|
+
id: tinyint("id").notNull().primaryKey().default(1),
|
|
11
|
+
toolProvider: varchar("tool_provider", { length: 32 }).notNull().default("claude"),
|
|
12
|
+
gcpLocation: varchar("gcp_location", { length: 32 }).notNull().default("us-east5"),
|
|
13
|
+
chatInterface: varchar("chat_interface", { length: 32 }).notNull().default("custom"),
|
|
14
|
+
updatedAt: datetime("updated_at", { mode: "string" }).notNull().default(sql`CURRENT_TIMESTAMP`),
|
|
15
|
+
updatedByUserId: bigintFkNullable("updated_by_user_id")
|
|
16
|
+
});
|
|
17
|
+
var chatSessions = mysqlTable(
|
|
18
|
+
"chat_sessions",
|
|
19
|
+
{
|
|
20
|
+
id: bigintPk(),
|
|
21
|
+
userId: bigintFk("user_id"),
|
|
22
|
+
title: varchar("title", { length: 200 }).notNull(),
|
|
23
|
+
createdAt: createdAt(),
|
|
24
|
+
updatedAt: datetime("updated_at", { mode: "string" }).notNull().default(sql`CURRENT_TIMESTAMP`)
|
|
25
|
+
},
|
|
26
|
+
(t) => ({
|
|
27
|
+
idxUserUpdated: index("idx_chat_session_user_updated").on(t.userId, t.updatedAt)
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
var chatMessageRoleEnum = ["user", "assistant"];
|
|
31
|
+
var chatMessages = mysqlTable(
|
|
32
|
+
"chat_messages",
|
|
33
|
+
{
|
|
34
|
+
id: bigintPk(),
|
|
35
|
+
sessionId: bigintFk("session_id"),
|
|
36
|
+
role: mysqlEnum("role", chatMessageRoleEnum).notNull(),
|
|
37
|
+
question: text("question"),
|
|
38
|
+
blocks: json("blocks"),
|
|
39
|
+
prose: json("prose"),
|
|
40
|
+
errorJson: json("error_json"),
|
|
41
|
+
createdAt: createdAt()
|
|
42
|
+
},
|
|
43
|
+
(t) => ({
|
|
44
|
+
idxSessionCreated: index("idx_chat_msg_session_created").on(t.sessionId, t.createdAt)
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
function mapSession(row) {
|
|
48
|
+
return {
|
|
49
|
+
id: row.id,
|
|
50
|
+
userId: row.userId,
|
|
51
|
+
title: row.title,
|
|
52
|
+
createdAt: new Date(row.createdAt),
|
|
53
|
+
updatedAt: new Date(row.updatedAt)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function mapMessage(row) {
|
|
57
|
+
return {
|
|
58
|
+
id: row.id,
|
|
59
|
+
sessionId: row.sessionId,
|
|
60
|
+
role: row.role,
|
|
61
|
+
question: row.question,
|
|
62
|
+
blocks: row.blocks,
|
|
63
|
+
prose: row.prose,
|
|
64
|
+
errorJson: row.errorJson,
|
|
65
|
+
createdAt: new Date(row.createdAt)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function mapSettings(row) {
|
|
69
|
+
return {
|
|
70
|
+
toolProvider: row.toolProvider,
|
|
71
|
+
gcpLocation: row.gcpLocation,
|
|
72
|
+
chatInterface: row.chatInterface,
|
|
73
|
+
updatedAt: row.updatedAt ? new Date(row.updatedAt) : null,
|
|
74
|
+
updatedByUserId: row.updatedByUserId
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function createDrizzlePersistence(db) {
|
|
78
|
+
return {
|
|
79
|
+
// ---------------------------------------------------------------------
|
|
80
|
+
// Sessions
|
|
81
|
+
// ---------------------------------------------------------------------
|
|
82
|
+
async createSession(input) {
|
|
83
|
+
const inserted = await db.insert(chatSessions).values({
|
|
84
|
+
userId: input.userId,
|
|
85
|
+
title: input.title
|
|
86
|
+
}).$returningId();
|
|
87
|
+
const id = inserted[0]?.id;
|
|
88
|
+
if (id == null) {
|
|
89
|
+
throw new Error("createSession: insert returned no id");
|
|
90
|
+
}
|
|
91
|
+
const [row] = await db.select().from(chatSessions).where(eq(chatSessions.id, id)).limit(1);
|
|
92
|
+
if (!row) {
|
|
93
|
+
throw new Error(`createSession: row ${id} not found after insert`);
|
|
94
|
+
}
|
|
95
|
+
return mapSession(row);
|
|
96
|
+
},
|
|
97
|
+
async getSession(id, userId) {
|
|
98
|
+
const [row] = await db.select().from(chatSessions).where(and(eq(chatSessions.id, id), eq(chatSessions.userId, userId))).limit(1);
|
|
99
|
+
return row ? mapSession(row) : null;
|
|
100
|
+
},
|
|
101
|
+
async listSessionsForUser(userId, opts) {
|
|
102
|
+
const limit = opts?.limit ?? 50;
|
|
103
|
+
const rows = await db.select().from(chatSessions).where(eq(chatSessions.userId, userId)).orderBy(desc(chatSessions.updatedAt)).limit(limit);
|
|
104
|
+
return rows.map(mapSession);
|
|
105
|
+
},
|
|
106
|
+
async updateSession(id, userId, patch) {
|
|
107
|
+
if (patch.title === void 0) return;
|
|
108
|
+
await db.update(chatSessions).set({ title: patch.title }).where(and(eq(chatSessions.id, id), eq(chatSessions.userId, userId)));
|
|
109
|
+
},
|
|
110
|
+
async deleteSession(id, userId) {
|
|
111
|
+
const owned = await db.select({ id: chatSessions.id }).from(chatSessions).where(and(eq(chatSessions.id, id), eq(chatSessions.userId, userId))).limit(1);
|
|
112
|
+
if (owned.length === 0) return;
|
|
113
|
+
await db.delete(chatMessages).where(eq(chatMessages.sessionId, id));
|
|
114
|
+
await db.delete(chatSessions).where(and(eq(chatSessions.id, id), eq(chatSessions.userId, userId)));
|
|
115
|
+
},
|
|
116
|
+
// ---------------------------------------------------------------------
|
|
117
|
+
// Messages
|
|
118
|
+
// ---------------------------------------------------------------------
|
|
119
|
+
async appendMessage(input) {
|
|
120
|
+
const inserted = await db.insert(chatMessages).values({
|
|
121
|
+
sessionId: input.sessionId,
|
|
122
|
+
role: input.role,
|
|
123
|
+
question: input.question ?? null,
|
|
124
|
+
blocks: input.blocks ?? null,
|
|
125
|
+
prose: input.prose ?? null,
|
|
126
|
+
errorJson: input.errorJson ?? null
|
|
127
|
+
}).$returningId();
|
|
128
|
+
const id = inserted[0]?.id;
|
|
129
|
+
if (id == null) {
|
|
130
|
+
throw new Error("appendMessage: insert returned no id");
|
|
131
|
+
}
|
|
132
|
+
const [row] = await db.select().from(chatMessages).where(eq(chatMessages.id, id)).limit(1);
|
|
133
|
+
if (!row) {
|
|
134
|
+
throw new Error(`appendMessage: row ${id} not found after insert`);
|
|
135
|
+
}
|
|
136
|
+
return mapMessage(row);
|
|
137
|
+
},
|
|
138
|
+
async listMessagesForSession(sessionId, userId) {
|
|
139
|
+
const owned = await db.select({ id: chatSessions.id }).from(chatSessions).where(
|
|
140
|
+
and(eq(chatSessions.id, sessionId), eq(chatSessions.userId, userId))
|
|
141
|
+
).limit(1);
|
|
142
|
+
if (owned.length === 0) return [];
|
|
143
|
+
const rows = await db.select().from(chatMessages).where(eq(chatMessages.sessionId, sessionId)).orderBy(chatMessages.createdAt, chatMessages.id);
|
|
144
|
+
return rows.map(mapMessage);
|
|
145
|
+
},
|
|
146
|
+
// ---------------------------------------------------------------------
|
|
147
|
+
// AI settings (singleton row)
|
|
148
|
+
// ---------------------------------------------------------------------
|
|
149
|
+
async getAiSettings() {
|
|
150
|
+
const [row] = await db.select().from(aiSettings).where(eq(aiSettings.id, 1)).limit(1);
|
|
151
|
+
if (!row) {
|
|
152
|
+
return {
|
|
153
|
+
toolProvider: "claude",
|
|
154
|
+
gcpLocation: "us-east5",
|
|
155
|
+
chatInterface: "custom",
|
|
156
|
+
updatedAt: null,
|
|
157
|
+
updatedByUserId: null
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return mapSettings(row);
|
|
161
|
+
},
|
|
162
|
+
async updateAiSettings(patch, byUserId) {
|
|
163
|
+
const insertValues = {
|
|
164
|
+
id: 1,
|
|
165
|
+
toolProvider: patch.toolProvider ?? "claude",
|
|
166
|
+
gcpLocation: patch.gcpLocation ?? "us-east5",
|
|
167
|
+
chatInterface: patch.chatInterface ?? "custom",
|
|
168
|
+
updatedByUserId: byUserId
|
|
169
|
+
};
|
|
170
|
+
const updateSet = {
|
|
171
|
+
updatedByUserId: byUserId
|
|
172
|
+
};
|
|
173
|
+
if (patch.toolProvider !== void 0) {
|
|
174
|
+
updateSet.toolProvider = patch.toolProvider;
|
|
175
|
+
}
|
|
176
|
+
if (patch.gcpLocation !== void 0) {
|
|
177
|
+
updateSet.gcpLocation = patch.gcpLocation;
|
|
178
|
+
}
|
|
179
|
+
if (patch.chatInterface !== void 0) {
|
|
180
|
+
updateSet.chatInterface = patch.chatInterface;
|
|
181
|
+
}
|
|
182
|
+
await db.insert(aiSettings).values(insertValues).onDuplicateKeyUpdate({ set: updateSet });
|
|
183
|
+
const [row] = await db.select().from(aiSettings).where(eq(aiSettings.id, 1)).limit(1);
|
|
184
|
+
if (!row) {
|
|
185
|
+
throw new Error("updateAiSettings: singleton row missing after upsert");
|
|
186
|
+
}
|
|
187
|
+
return mapSettings(row);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export { aiSettings, chatMessages, chatSessions, createDrizzlePersistence };
|
|
193
|
+
//# sourceMappingURL=index.js.map
|
|
194
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/drizzle/tables.ts","../../src/adapters/drizzle/adapter.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Canonical drizzle MySQL table definitions for `@firstlovecenter/ai-chat`.\n *\n * These three tables (`chat_sessions`, `chat_messages`, `ai_settings`) are\n * the data shape the package needs to persist its conversations and global\n * config. The host re-exports them from its own schema so that an existing\n * deployment keeps the same column names, lengths, FKs, and indexes — making\n * the host's data fully portable into and out of the package.\n *\n * Deviations from the host's prior shape are deliberate:\n * - `tool_provider` and `gcp_location` are plain VARCHAR (not enums) so a\n * consumer can register additional providers / regions without a schema\n * migration. Validation against the runtime registries happens at a\n * higher layer.\n * - `chat_interface` is a new VARCHAR column controlling which chat UI\n * (custom vs. vercel-ai) renders globally; default 'custom'.\n *\n * The small column helpers from the host (`bigintPk`, `bigintFk`,\n * `bigintFkNullable`, `createdAt`) are re-defined inline here — this package\n * never imports from the host repo.\n */\nimport { sql } from 'drizzle-orm';\nimport {\n bigint,\n datetime,\n index,\n json,\n mysqlEnum,\n mysqlTable,\n text,\n tinyint,\n varchar\n} from 'drizzle-orm/mysql-core';\n\n// ---------------------------------------------------------------------------\n// Inline column helpers (mirrors host `src/db/columns.ts` shapes)\n// ---------------------------------------------------------------------------\n\n/** BIGINT UNSIGNED PK with auto-increment, returned as `number`. */\nconst bigintPk = () =>\n bigint('id', { mode: 'number', unsigned: true })\n .notNull()\n .primaryKey()\n .autoincrement();\n\n/** NOT NULL BIGINT UNSIGNED FK column, returned as `number`. */\nconst bigintFk = (name: string) =>\n bigint(name, { mode: 'number', unsigned: true }).notNull();\n\n/** Nullable BIGINT UNSIGNED FK column, returned as `number | null`. */\nconst bigintFkNullable = (name: string) =>\n bigint(name, { mode: 'number', unsigned: true });\n\n/** `created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP`, mode 'string'. */\nconst createdAt = () =>\n datetime('created_at', { mode: 'string' })\n .notNull()\n .default(sql`CURRENT_TIMESTAMP`);\n\n// ---------------------------------------------------------------------------\n// ai_settings (singleton — always one row at id=1)\n// ---------------------------------------------------------------------------\n\n/**\n * Global AI configuration. Singleton row enforced by `id=1` default + PK.\n * `tool_provider`, `gcp_location`, and `chat_interface` are open VARCHARs;\n * the runtime layer validates against the host's registries before write.\n */\nexport const aiSettings = mysqlTable('ai_settings', {\n id: tinyint('id').notNull().primaryKey().default(1),\n toolProvider: varchar('tool_provider', { length: 32 })\n .notNull()\n .default('claude'),\n gcpLocation: varchar('gcp_location', { length: 32 })\n .notNull()\n .default('us-east5'),\n chatInterface: varchar('chat_interface', { length: 32 })\n .notNull()\n .default('custom'),\n updatedAt: datetime('updated_at', { mode: 'string' })\n .notNull()\n .default(sql`CURRENT_TIMESTAMP`),\n updatedByUserId: bigintFkNullable('updated_by_user_id')\n});\n\n// ---------------------------------------------------------------------------\n// chat_sessions (one per conversation)\n// ---------------------------------------------------------------------------\n\nexport const chatSessions = mysqlTable(\n 'chat_sessions',\n {\n id: bigintPk(),\n userId: bigintFk('user_id'),\n title: varchar('title', { length: 200 }).notNull(),\n createdAt: createdAt(),\n updatedAt: datetime('updated_at', { mode: 'string' })\n .notNull()\n .default(sql`CURRENT_TIMESTAMP`)\n },\n (t) => ({\n idxUserUpdated: index('idx_chat_session_user_updated').on(t.userId, t.updatedAt)\n })\n);\n\n// ---------------------------------------------------------------------------\n// chat_messages (one row per turn in a chat_session)\n// ---------------------------------------------------------------------------\n\nexport const chatMessageRoleEnum = ['user', 'assistant'] as const;\n\nexport const chatMessages = mysqlTable(\n 'chat_messages',\n {\n id: bigintPk(),\n sessionId: bigintFk('session_id'),\n role: mysqlEnum('role', chatMessageRoleEnum).notNull(),\n question: text('question'),\n blocks: json('blocks'),\n prose: json('prose'),\n errorJson: json('error_json'),\n createdAt: createdAt()\n },\n (t) => ({\n idxSessionCreated: index('idx_chat_msg_session_created').on(t.sessionId, t.createdAt)\n })\n);\n\n// ---------------------------------------------------------------------------\n// Inferred row types — distinct from the domain types in ports/types.ts.\n// Adapters map these row shapes (datetime as string) to the domain shapes\n// (Date) at the boundary.\n// ---------------------------------------------------------------------------\n\nexport type ChatSessionRow = typeof chatSessions.$inferSelect;\nexport type ChatMessageRow = typeof chatMessages.$inferSelect;\nexport type AiSettingsRow = typeof aiSettings.$inferSelect;\n","/**\n * Drizzle MySQL implementation of `PersistencePort`.\n *\n * The package never imports a concrete drizzle client — the host passes a\n * `MySql2Database` (or any compatible drizzle MySQL handle) and we use it\n * to issue the queries this port describes.\n *\n * Boundary mapping: the drizzle MySQL `datetime` columns are declared with\n * `mode: 'string'` to match the host's existing schema, so row reads return\n * ISO strings. The PersistencePort domain types use `Date`, so every row\n * crossing the boundary is converted via `new Date(row.createdAt)`.\n *\n * Per-user safety: `getSession`, `updateSession`, `deleteSession`, and\n * `listMessagesForSession` all join `userId` into the WHERE clause — the\n * port's contract is that no caller should be able to read or mutate\n * another user's data even if they've forged a session id.\n */\nimport { and, desc, eq } from 'drizzle-orm';\nimport type { MySql2Database } from 'drizzle-orm/mysql2';\n\nimport type {\n AiSettings,\n AiSettingsPatch,\n AppendMessageInput,\n ChatMessage,\n ChatMessageRole,\n ChatSession,\n CreateSessionInput,\n ListSessionsOpts,\n PersistencePort\n} from '../../server/ports/types';\n\nimport {\n aiSettings,\n chatMessages,\n chatSessions,\n type AiSettingsRow,\n type ChatMessageRow,\n type ChatSessionRow\n} from './tables';\n\n// ---------------------------------------------------------------------------\n// Row -> domain mappers\n// ---------------------------------------------------------------------------\n\nfunction mapSession(row: ChatSessionRow): ChatSession {\n return {\n id: row.id,\n userId: row.userId,\n title: row.title,\n createdAt: new Date(row.createdAt),\n updatedAt: new Date(row.updatedAt)\n };\n}\n\nfunction mapMessage(row: ChatMessageRow): ChatMessage {\n return {\n id: row.id,\n sessionId: row.sessionId,\n role: row.role as ChatMessageRole,\n question: row.question,\n blocks: row.blocks,\n prose: row.prose,\n errorJson: row.errorJson,\n createdAt: new Date(row.createdAt)\n };\n}\n\nfunction mapSettings(row: AiSettingsRow): AiSettings {\n return {\n toolProvider: row.toolProvider,\n gcpLocation: row.gcpLocation,\n chatInterface: row.chatInterface,\n updatedAt: row.updatedAt ? new Date(row.updatedAt) : null,\n updatedByUserId: row.updatedByUserId\n };\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createDrizzlePersistence(\n db: MySql2Database<any>\n): PersistencePort {\n return {\n // ---------------------------------------------------------------------\n // Sessions\n // ---------------------------------------------------------------------\n\n async createSession(input: CreateSessionInput): Promise<ChatSession> {\n const inserted = await db\n .insert(chatSessions)\n .values({\n userId: input.userId,\n title: input.title\n })\n .$returningId();\n\n const id = inserted[0]?.id;\n if (id == null) {\n throw new Error('createSession: insert returned no id');\n }\n\n const [row] = await db\n .select()\n .from(chatSessions)\n .where(eq(chatSessions.id, id))\n .limit(1);\n\n if (!row) {\n throw new Error(`createSession: row ${id} not found after insert`);\n }\n return mapSession(row);\n },\n\n async getSession(id: number, userId: number): Promise<ChatSession | null> {\n const [row] = await db\n .select()\n .from(chatSessions)\n .where(and(eq(chatSessions.id, id), eq(chatSessions.userId, userId)))\n .limit(1);\n\n return row ? mapSession(row) : null;\n },\n\n async listSessionsForUser(\n userId: number,\n opts?: ListSessionsOpts\n ): Promise<ChatSession[]> {\n const limit = opts?.limit ?? 50;\n const rows = await db\n .select()\n .from(chatSessions)\n .where(eq(chatSessions.userId, userId))\n .orderBy(desc(chatSessions.updatedAt))\n .limit(limit);\n\n return rows.map(mapSession);\n },\n\n async updateSession(\n id: number,\n userId: number,\n patch: { title?: string }\n ): Promise<void> {\n if (patch.title === undefined) return;\n\n await db\n .update(chatSessions)\n .set({ title: patch.title })\n .where(and(eq(chatSessions.id, id), eq(chatSessions.userId, userId)));\n },\n\n async deleteSession(id: number, userId: number): Promise<void> {\n // Delete messages first (no ON DELETE CASCADE assumed at table level).\n const owned = await db\n .select({ id: chatSessions.id })\n .from(chatSessions)\n .where(and(eq(chatSessions.id, id), eq(chatSessions.userId, userId)))\n .limit(1);\n\n if (owned.length === 0) return;\n\n await db.delete(chatMessages).where(eq(chatMessages.sessionId, id));\n await db\n .delete(chatSessions)\n .where(and(eq(chatSessions.id, id), eq(chatSessions.userId, userId)));\n },\n\n // ---------------------------------------------------------------------\n // Messages\n // ---------------------------------------------------------------------\n\n async appendMessage(input: AppendMessageInput): Promise<ChatMessage> {\n const inserted = await db\n .insert(chatMessages)\n .values({\n sessionId: input.sessionId,\n role: input.role,\n question: input.question ?? null,\n blocks: input.blocks ?? null,\n prose: input.prose ?? null,\n errorJson: input.errorJson ?? null\n })\n .$returningId();\n\n const id = inserted[0]?.id;\n if (id == null) {\n throw new Error('appendMessage: insert returned no id');\n }\n\n const [row] = await db\n .select()\n .from(chatMessages)\n .where(eq(chatMessages.id, id))\n .limit(1);\n\n if (!row) {\n throw new Error(`appendMessage: row ${id} not found after insert`);\n }\n return mapMessage(row);\n },\n\n async listMessagesForSession(\n sessionId: number,\n userId: number\n ): Promise<ChatMessage[]> {\n // Verify ownership before returning rows — never trust the caller to\n // have filtered already.\n const owned = await db\n .select({ id: chatSessions.id })\n .from(chatSessions)\n .where(\n and(eq(chatSessions.id, sessionId), eq(chatSessions.userId, userId))\n )\n .limit(1);\n\n if (owned.length === 0) return [];\n\n const rows = await db\n .select()\n .from(chatMessages)\n .where(eq(chatMessages.sessionId, sessionId))\n .orderBy(chatMessages.createdAt, chatMessages.id);\n\n return rows.map(mapMessage);\n },\n\n // ---------------------------------------------------------------------\n // AI settings (singleton row)\n // ---------------------------------------------------------------------\n\n async getAiSettings(): Promise<AiSettings> {\n const [row] = await db\n .select()\n .from(aiSettings)\n .where(eq(aiSettings.id, 1))\n .limit(1);\n\n if (!row) {\n // Synthesize defaults rather than failing — the row is created on\n // first write via INSERT … ON DUPLICATE KEY UPDATE.\n return {\n toolProvider: 'claude',\n gcpLocation: 'us-east5',\n chatInterface: 'custom',\n updatedAt: null,\n updatedByUserId: null\n };\n }\n\n return mapSettings(row);\n },\n\n async updateAiSettings(\n patch: AiSettingsPatch,\n byUserId: number\n ): Promise<AiSettings> {\n // Build the values object with the singleton id and any defaults the\n // INSERT branch needs. ON DUPLICATE KEY UPDATE only touches keys in\n // the patch (plus updated_at / updated_by_user_id audit columns).\n const insertValues = {\n id: 1 as const,\n toolProvider: patch.toolProvider ?? 'claude',\n gcpLocation: patch.gcpLocation ?? 'us-east5',\n chatInterface: patch.chatInterface ?? 'custom',\n updatedByUserId: byUserId\n };\n\n const updateSet: Record<string, unknown> = {\n updatedByUserId: byUserId\n };\n if (patch.toolProvider !== undefined) {\n updateSet.toolProvider = patch.toolProvider;\n }\n if (patch.gcpLocation !== undefined) {\n updateSet.gcpLocation = patch.gcpLocation;\n }\n if (patch.chatInterface !== undefined) {\n updateSet.chatInterface = patch.chatInterface;\n }\n\n await db\n .insert(aiSettings)\n .values(insertValues)\n .onDuplicateKeyUpdate({ set: updateSet });\n\n const [row] = await db\n .select()\n .from(aiSettings)\n .where(eq(aiSettings.id, 1))\n .limit(1);\n\n if (!row) {\n throw new Error('updateAiSettings: singleton row missing after upsert');\n }\n return mapSettings(row);\n }\n };\n}\n"]}
|