@firstlovecenter/ai-chat 0.8.1 → 0.9.1

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.
@@ -114,8 +114,14 @@ type SystemBlock = {
114
114
  * regardless of the chosen ORM.
115
115
  */
116
116
 
117
+ /**
118
+ * Chat session and message identifiers are short URL-safe UIDs (12 chars,
119
+ * ~72 bits of entropy, alphabet matches nanoid's URL-safe set). The
120
+ * package generates them in `createSession`/`appendMessage` so adapters
121
+ * never need to know about auto-increment vs UID.
122
+ */
117
123
  type ChatSession = {
118
- id: number;
124
+ id: string;
119
125
  userId: number;
120
126
  title: string;
121
127
  createdAt: Date;
@@ -129,8 +135,8 @@ type ChatMessageRole = 'user' | 'assistant';
129
135
  * narrator. `errorJson` is set when an assistant turn failed mid-stream.
130
136
  */
131
137
  type ChatMessage = {
132
- id: number;
133
- sessionId: number;
138
+ id: string;
139
+ sessionId: string;
134
140
  role: ChatMessageRole;
135
141
  question: string | null;
136
142
  blocks: unknown | null;
@@ -244,7 +250,7 @@ type CreateSessionInput = {
244
250
  title: string;
245
251
  };
246
252
  type AppendMessageInput = {
247
- sessionId: number;
253
+ sessionId: string;
248
254
  role: ChatMessageRole;
249
255
  question?: string | null;
250
256
  blocks?: unknown | null;
@@ -277,15 +283,15 @@ type AiSettingsPatch = {
277
283
  type PersistencePort = {
278
284
  createSession(input: CreateSessionInput): Promise<ChatSession>;
279
285
  /** Returns null when the session doesn't exist OR doesn't belong to this user. */
280
- getSession(id: number, userId: number): Promise<ChatSession | null>;
286
+ getSession(id: string, userId: number): Promise<ChatSession | null>;
281
287
  listSessionsForUser(userId: number, opts?: ListSessionsOpts): Promise<ChatSession[]>;
282
288
  /** No-op when session doesn't exist or doesn't belong to user — caller asserted authorisation. */
283
- updateSession(id: number, userId: number, patch: {
289
+ updateSession(id: string, userId: number, patch: {
284
290
  title?: string;
285
291
  }): Promise<void>;
286
- deleteSession(id: number, userId: number): Promise<void>;
292
+ deleteSession(id: string, userId: number): Promise<void>;
287
293
  appendMessage(input: AppendMessageInput): Promise<ChatMessage>;
288
- listMessagesForSession(sessionId: number, userId: number): Promise<ChatMessage[]>;
294
+ listMessagesForSession(sessionId: string, userId: number): Promise<ChatMessage[]>;
289
295
  /** Returns the singleton row, applying defaults if it's never been written. */
290
296
  getAiSettings(): Promise<AiSettings>;
291
297
  /**
@@ -114,8 +114,14 @@ type SystemBlock = {
114
114
  * regardless of the chosen ORM.
115
115
  */
116
116
 
117
+ /**
118
+ * Chat session and message identifiers are short URL-safe UIDs (12 chars,
119
+ * ~72 bits of entropy, alphabet matches nanoid's URL-safe set). The
120
+ * package generates them in `createSession`/`appendMessage` so adapters
121
+ * never need to know about auto-increment vs UID.
122
+ */
117
123
  type ChatSession = {
118
- id: number;
124
+ id: string;
119
125
  userId: number;
120
126
  title: string;
121
127
  createdAt: Date;
@@ -129,8 +135,8 @@ type ChatMessageRole = 'user' | 'assistant';
129
135
  * narrator. `errorJson` is set when an assistant turn failed mid-stream.
130
136
  */
131
137
  type ChatMessage = {
132
- id: number;
133
- sessionId: number;
138
+ id: string;
139
+ sessionId: string;
134
140
  role: ChatMessageRole;
135
141
  question: string | null;
136
142
  blocks: unknown | null;
@@ -244,7 +250,7 @@ type CreateSessionInput = {
244
250
  title: string;
245
251
  };
246
252
  type AppendMessageInput = {
247
- sessionId: number;
253
+ sessionId: string;
248
254
  role: ChatMessageRole;
249
255
  question?: string | null;
250
256
  blocks?: unknown | null;
@@ -277,15 +283,15 @@ type AiSettingsPatch = {
277
283
  type PersistencePort = {
278
284
  createSession(input: CreateSessionInput): Promise<ChatSession>;
279
285
  /** Returns null when the session doesn't exist OR doesn't belong to this user. */
280
- getSession(id: number, userId: number): Promise<ChatSession | null>;
286
+ getSession(id: string, userId: number): Promise<ChatSession | null>;
281
287
  listSessionsForUser(userId: number, opts?: ListSessionsOpts): Promise<ChatSession[]>;
282
288
  /** No-op when session doesn't exist or doesn't belong to user — caller asserted authorisation. */
283
- updateSession(id: number, userId: number, patch: {
289
+ updateSession(id: string, userId: number, patch: {
284
290
  title?: string;
285
291
  }): Promise<void>;
286
- deleteSession(id: number, userId: number): Promise<void>;
292
+ deleteSession(id: string, userId: number): Promise<void>;
287
293
  appendMessage(input: AppendMessageInput): Promise<ChatMessage>;
288
- listMessagesForSession(sessionId: number, userId: number): Promise<ChatMessage[]>;
294
+ listMessagesForSession(sessionId: string, userId: number): Promise<ChatMessage[]>;
289
295
  /** Returns the singleton row, applying defaults if it's never been written. */
290
296
  getAiSettings(): Promise<AiSettings>;
291
297
  /**
package/dist/ui/index.cjs CHANGED
@@ -678,7 +678,8 @@ function AiChat({
678
678
  userFirstName,
679
679
  scopeLabel,
680
680
  initialProvider,
681
- initialSessionId = null
681
+ initialSessionId = null,
682
+ basePath = "/chat"
682
683
  }) {
683
684
  const router = navigation.useRouter();
684
685
  const [sessions, setSessions] = React.useState([]);
@@ -768,16 +769,16 @@ function AiChat({
768
769
  }, []);
769
770
  const syncUrl = React.useCallback(
770
771
  (id) => {
771
- router.push(id == null ? "/chat" : `/chat/${id}`);
772
+ router.push(id == null ? basePath : `${basePath}/${id}`);
772
773
  },
773
- [router]
774
+ [router, basePath]
774
775
  );
775
776
  const newChat = React.useCallback(() => {
776
777
  setActiveSessionId(null);
777
778
  setAnswers([]);
778
779
  setQuestion("");
779
- router.push("/chat?new");
780
- }, [router]);
780
+ router.push(`${basePath}?new`);
781
+ }, [router, basePath]);
781
782
  const changeProvider = React.useCallback(
782
783
  async (next) => {
783
784
  if (next === provider || providerSaving) return;
@@ -1030,7 +1031,7 @@ function AiChat({
1030
1031
  /* @__PURE__ */ jsxRuntime.jsx(
1031
1032
  Link__default.default,
1032
1033
  {
1033
- href: `/chat/${s.id}`,
1034
+ href: `${basePath}/${s.id}`,
1034
1035
  onClick: () => setSidebarOpen(false),
1035
1036
  className: cn(
1036
1037
  "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
@@ -1513,7 +1514,8 @@ function VercelChat({
1513
1514
  userFirstName,
1514
1515
  scopeLabel,
1515
1516
  initialProvider,
1516
- initialSessionId = null
1517
+ initialSessionId = null,
1518
+ basePath = "/chat"
1517
1519
  }) {
1518
1520
  const router = navigation.useRouter();
1519
1521
  const [sessions, setSessions] = React.useState([]);
@@ -1744,9 +1746,9 @@ function VercelChat({
1744
1746
  }, [answers.length]);
1745
1747
  const syncUrl = React.useCallback(
1746
1748
  (id) => {
1747
- router.push(id == null ? "/chat" : `/chat/${id}`);
1749
+ router.push(id == null ? basePath : `${basePath}/${id}`);
1748
1750
  },
1749
- [router]
1751
+ [router, basePath]
1750
1752
  );
1751
1753
  const newChat = React.useCallback(() => {
1752
1754
  setActiveSessionId(null);
@@ -1756,8 +1758,8 @@ function VercelChat({
1756
1758
  setHydratedErrors({});
1757
1759
  setStartedAt({});
1758
1760
  setInput("");
1759
- router.push("/chat?new");
1760
- }, [setMessages, setInput, router]);
1761
+ router.push(`${basePath}?new`);
1762
+ }, [setMessages, setInput, router, basePath]);
1761
1763
  const changeProvider = React.useCallback(
1762
1764
  async (next) => {
1763
1765
  if (next === provider || providerSaving) return;
@@ -1987,7 +1989,7 @@ function VercelChat({
1987
1989
  /* @__PURE__ */ jsxRuntime.jsx(
1988
1990
  Link__default.default,
1989
1991
  {
1990
- href: `/chat/${s.id}`,
1992
+ href: `${basePath}/${s.id}`,
1991
1993
  onClick: () => setSidebarOpen(false),
1992
1994
  className: cn(
1993
1995
  "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",