@hobenakicoffee/libraries 8.1.0 → 8.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hobenakicoffee/libraries",
3
- "version": "8.1.0",
3
+ "version": "8.2.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "types": "src/index.ts",
package/src/App.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import { useState } from "react";
2
2
  import { productInfo } from "@/constants/legal";
3
+ import SchemaViewer from "@/SchemaViewer";
3
4
  import logoSvg from "/favicon.svg";
4
5
 
5
6
  declare const __LATEST_VERSION__: string;
@@ -39,7 +40,10 @@ const EXPORTS = [
39
40
  { path: "/docs", name: "docs", desc: "Documentation & guides" },
40
41
  ];
41
42
 
43
+ type Page = "home" | "schema";
44
+
42
45
  const App = () => {
46
+ const [page, setPage] = useState<Page>("home");
43
47
  const [copied, setCopied] = useState(false);
44
48
 
45
49
  const handleCopy = () => {
@@ -48,6 +52,10 @@ const App = () => {
48
52
  setTimeout(() => setCopied(false), 2000);
49
53
  };
50
54
 
55
+ if (page === "schema") {
56
+ return <SchemaViewer onBack={() => setPage("home")} />;
57
+ }
58
+
51
59
  return (
52
60
  <div className="min-dvh relative flex flex-col overflow-hidden bg-[#0a0c10] text-slate-200">
53
61
  <div className="pointer-events-none absolute inset-0 overflow-hidden">
@@ -76,9 +84,18 @@ const App = () => {
76
84
  </p>
77
85
  </div>
78
86
  </div>
79
- <div className="flex items-center gap-2 rounded-md border border-slate-800 bg-slate-900/50 px-2 py-1 font-mono text-slate-400 text-xs">
80
- <span className="h-2 w-2 rounded-full bg-green-500" />
81
- TypeScript
87
+ <div className="flex items-center gap-3">
88
+ <button
89
+ className="cursor-pointer rounded-md border border-slate-700 bg-slate-900/50 px-3 py-1.5 font-mono text-slate-400 text-xs transition-colors hover:border-slate-500 hover:text-slate-200"
90
+ onClick={() => setPage("schema")}
91
+ type="button"
92
+ >
93
+ schema
94
+ </button>
95
+ <div className="flex items-center gap-2 rounded-md border border-slate-800 bg-slate-900/50 px-2 py-1 font-mono text-slate-400 text-xs">
96
+ <span className="h-2 w-2 rounded-full bg-green-500" />
97
+ TypeScript
98
+ </div>
82
99
  </div>
83
100
  </header>
84
101
 
@@ -0,0 +1,616 @@
1
+ import { schema } from "virtual:supabase-schema";
2
+ import { useMemo, useState } from "react";
3
+
4
+ type SelectedItem =
5
+ | { type: "table"; name: string }
6
+ | { type: "view"; name: string }
7
+ | { type: "enum"; name: string };
8
+
9
+ const tableNames = Object.keys(schema.tables);
10
+ const viewNames = Object.keys(schema.views);
11
+ const enumNames = Object.keys(schema.enums);
12
+
13
+ const SectionIcon = ({ label }: { label: string }) => {
14
+ if (label === "Tables") return <span className="text-blue-400">&#9654;</span>;
15
+ if (label === "Views")
16
+ return <span className="text-emerald-400">&#9654;</span>;
17
+ if (label === "Enums") return <span className="text-amber-400">&#9654;</span>;
18
+ return null;
19
+ };
20
+
21
+ const EnumBadge = ({ values }: { values: string[] }) => (
22
+ <span
23
+ className="cursor-help rounded border border-amber-700/40 bg-amber-950/40 px-1.5 py-0.5 font-mono text-[10px] text-amber-300"
24
+ title={values.join(", ")}
25
+ >
26
+ enum
27
+ </span>
28
+ );
29
+
30
+ const SerialBadge = () => (
31
+ <span className="rounded border border-purple-700/40 bg-purple-950/40 px-1.5 py-0.5 font-mono text-[10px] text-purple-300">
32
+ serial
33
+ </span>
34
+ );
35
+
36
+ const NullableBadge = () => (
37
+ <span className="rounded border border-slate-600/40 bg-slate-800/40 px-1.5 py-0.5 font-mono text-[10px] text-slate-400">
38
+ null
39
+ </span>
40
+ );
41
+
42
+ const BackButton = ({ onClick }: { onClick: () => void }) => (
43
+ <button
44
+ className="flex cursor-pointer items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-900 px-3 py-1.5 font-mono text-slate-400 text-xs transition-colors hover:border-slate-500 hover:text-slate-200"
45
+ onClick={onClick}
46
+ type="button"
47
+ >
48
+ <span>&larr;</span>
49
+ <span>Back</span>
50
+ </button>
51
+ );
52
+
53
+ const SchemaViewer = ({ onBack }: { onBack: () => void }) => {
54
+ const [selected, setSelected] = useState<SelectedItem | null>(null);
55
+ const [search, setSearch] = useState("");
56
+ const [collapsed, setCollapsed] = useState<Record<string, boolean>>({
57
+ Tables: false,
58
+ Views: false,
59
+ Enums: false,
60
+ });
61
+ const [sidebarOpen, setSidebarOpen] = useState(false);
62
+
63
+ const query = search.toLowerCase();
64
+
65
+ const filteredTables = useMemo(
66
+ () => tableNames.filter((n) => n.includes(query)),
67
+ [query]
68
+ );
69
+ const filteredViews = useMemo(
70
+ () => viewNames.filter((n) => n.includes(query)),
71
+ [query]
72
+ );
73
+ const filteredEnums = useMemo(
74
+ () => enumNames.filter((n) => n.includes(query)),
75
+ [query]
76
+ );
77
+
78
+ const hasTables = filteredTables.length > 0;
79
+ const hasViews = filteredViews.length > 0;
80
+ const hasEnums = filteredEnums.length > 0;
81
+
82
+ const toggleSection = (section: string) => {
83
+ setCollapsed((prev) => ({ ...prev, [section]: !prev[section] }));
84
+ };
85
+
86
+ const handleSelect = (item: SelectedItem) => {
87
+ setSelected(item);
88
+ setSidebarOpen(false);
89
+ };
90
+
91
+ const handleFKClick = (tableName: string) => {
92
+ setSelected({ type: "table", name: tableName });
93
+ };
94
+
95
+ const sel = selected;
96
+ const selectedTable =
97
+ sel !== null && sel.type === "table" ? schema.tables[sel.name] : undefined;
98
+ const selectedView =
99
+ sel !== null && sel.type === "view" ? schema.views[sel.name] : undefined;
100
+ const selectedEnum =
101
+ sel !== null && sel.type === "enum"
102
+ ? { name: sel.name, values: schema.enums[sel.name] ?? [] }
103
+ : undefined;
104
+
105
+ const getEnumValues = (enumName: string): string[] | undefined =>
106
+ schema.enums[enumName];
107
+
108
+ return (
109
+ <div className="flex h-dvh flex-col bg-[#0a0c10] text-slate-200">
110
+ <header className="flex shrink-0 items-center justify-between border-slate-800 border-b px-6 py-3">
111
+ <div className="flex items-center gap-4">
112
+ <BackButton onClick={onBack} />
113
+ <h1 className="font-mono font-semibold text-sm">
114
+ <span className="text-brand">$</span> supabase schema
115
+ </h1>
116
+ </div>
117
+ <div className="flex items-center gap-3">
118
+ {selected && (
119
+ <span className="hidden font-mono text-slate-500 text-xs sm:inline">
120
+ {selected.type} / {selected.name}
121
+ </span>
122
+ )}
123
+ <button
124
+ className="flex cursor-pointer items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-900 px-3 py-1.5 font-mono text-slate-400 text-xs transition-colors hover:border-slate-500 hover:text-slate-200 lg:hidden"
125
+ onClick={() => setSidebarOpen(true)}
126
+ type="button"
127
+ >
128
+ <svg
129
+ aria-label="Menu"
130
+ className="h-4 w-4"
131
+ fill="none"
132
+ role="img"
133
+ stroke="currentColor"
134
+ strokeWidth="2"
135
+ viewBox="0 0 24 24"
136
+ >
137
+ <title>Open sidebar</title>
138
+ <path d="M4 6h16M4 12h16M4 18h16" strokeLinecap="round" />
139
+ </svg>
140
+ <span>Tables</span>
141
+ </button>
142
+ </div>
143
+ </header>
144
+
145
+ <div className="flex min-h-0 flex-1">
146
+ {sidebarOpen && (
147
+ <button
148
+ aria-label="Close sidebar"
149
+ className="fixed inset-0 z-30 cursor-default bg-black/60 lg:hidden"
150
+ onClick={() => setSidebarOpen(false)}
151
+ type="button"
152
+ />
153
+ )}
154
+
155
+ <aside
156
+ className={`w-72 shrink-0 overflow-y-auto border-slate-800 border-r bg-slate-950 p-4 ${
157
+ sidebarOpen
158
+ ? "fixed inset-y-0 left-0 z-40 block"
159
+ : "hidden lg:block"
160
+ }`}
161
+ >
162
+ <SidebarContent
163
+ collapsed={collapsed}
164
+ filteredEnums={filteredEnums}
165
+ filteredTables={filteredTables}
166
+ filteredViews={filteredViews}
167
+ handleSelect={handleSelect}
168
+ hasEnums={hasEnums}
169
+ hasTables={hasTables}
170
+ hasViews={hasViews}
171
+ search={search}
172
+ selected={selected}
173
+ setSearch={setSearch}
174
+ toggleSection={toggleSection}
175
+ />
176
+ </aside>
177
+
178
+ <main className="flex-1 overflow-y-auto p-6">
179
+ {!selected && (
180
+ <div className="flex items-center justify-center pt-24">
181
+ <div className="text-center">
182
+ <div className="mb-4 font-mono text-4xl text-slate-600">
183
+ {"{ : }"}
184
+ </div>
185
+ <p className="font-mono text-slate-500 text-sm">
186
+ select a table, view, or enum from the sidebar
187
+ </p>
188
+ </div>
189
+ </div>
190
+ )}
191
+
192
+ {selectedTable && sel && (
193
+ <TableDetail
194
+ enumValues={getEnumValues}
195
+ onFKClick={handleFKClick}
196
+ relationships={selectedTable.relationships}
197
+ tableName={sel.name}
198
+ >
199
+ <ColumnsTable
200
+ columns={selectedTable.columns}
201
+ enumValues={getEnumValues}
202
+ />
203
+ </TableDetail>
204
+ )}
205
+
206
+ {selectedView && sel && (
207
+ <ViewDetail viewName={sel.name}>
208
+ <ColumnsTable
209
+ columns={selectedView.columns}
210
+ enumValues={getEnumValues}
211
+ />
212
+ </ViewDetail>
213
+ )}
214
+
215
+ {selectedEnum && (
216
+ <EnumDetail name={selectedEnum.name} values={selectedEnum.values} />
217
+ )}
218
+ </main>
219
+ </div>
220
+ </div>
221
+ );
222
+ };
223
+
224
+ const SidebarSection = ({
225
+ children,
226
+ collapsed,
227
+ count,
228
+ label,
229
+ onToggle,
230
+ }: {
231
+ children: React.ReactNode;
232
+ collapsed: boolean;
233
+ count: number;
234
+ label: string;
235
+ onToggle: () => void;
236
+ }) => (
237
+ <div className="mb-3">
238
+ <button
239
+ className="flex w-full cursor-pointer items-center gap-2 rounded px-1 py-1.5 font-mono text-slate-400 text-xs transition-colors hover:text-slate-200"
240
+ onClick={onToggle}
241
+ type="button"
242
+ >
243
+ <span className={`transition-transform ${collapsed ? "-rotate-90" : ""}`}>
244
+ <SectionIcon label={label} />
245
+ </span>
246
+ <span>{label}</span>
247
+ <span className="ml-auto rounded bg-slate-800 px-1.5 py-0.5 text-slate-500">
248
+ {count}
249
+ </span>
250
+ </button>
251
+ {!collapsed && <div className="mt-1 ml-2 space-y-0.5">{children}</div>}
252
+ </div>
253
+ );
254
+
255
+ const SidebarItem = ({
256
+ isActive,
257
+ label,
258
+ onClick,
259
+ }: {
260
+ isActive: boolean;
261
+ label: string;
262
+ onClick: () => void;
263
+ }) => (
264
+ <button
265
+ className={`w-full cursor-pointer rounded px-3 py-1.5 text-left font-mono text-xs transition-colors ${
266
+ isActive
267
+ ? "bg-brand/10 text-brand"
268
+ : "text-slate-400 hover:bg-slate-800/50 hover:text-slate-200"
269
+ }`}
270
+ onClick={onClick}
271
+ type="button"
272
+ >
273
+ {label}
274
+ </button>
275
+ );
276
+
277
+ const SidebarContent = ({
278
+ collapsed,
279
+ filteredEnums,
280
+ filteredTables,
281
+ filteredViews,
282
+ handleSelect,
283
+ hasEnums,
284
+ hasTables,
285
+ hasViews,
286
+ search,
287
+ selected,
288
+ setSearch,
289
+ toggleSection,
290
+ }: {
291
+ collapsed: Record<string, boolean>;
292
+ filteredEnums: string[];
293
+ filteredTables: string[];
294
+ filteredViews: string[];
295
+ handleSelect: (item: SelectedItem) => void;
296
+ hasEnums: boolean;
297
+ hasTables: boolean;
298
+ hasViews: boolean;
299
+ search: string;
300
+ selected: SelectedItem | null;
301
+ setSearch: (v: string) => void;
302
+ toggleSection: (section: string) => void;
303
+ }) => (
304
+ <>
305
+ <input
306
+ className="mb-4 w-full rounded-lg border border-slate-700 bg-slate-900 px-3 py-2 font-mono text-slate-200 text-xs placeholder-slate-500 outline-none focus:border-brand/50"
307
+ onChange={(e) => setSearch(e.target.value)}
308
+ placeholder="filter tables, views, enums..."
309
+ type="text"
310
+ value={search}
311
+ />
312
+
313
+ {!(hasTables || hasViews || hasEnums) && (
314
+ <p className="font-mono text-slate-500 text-xs">no results</p>
315
+ )}
316
+
317
+ {hasTables && (
318
+ <SidebarSection
319
+ collapsed={collapsed.Tables ?? false}
320
+ count={filteredTables.length}
321
+ label="Tables"
322
+ onToggle={() => toggleSection("Tables")}
323
+ >
324
+ {filteredTables.map((name) => (
325
+ <SidebarItem
326
+ isActive={selected?.type === "table" && selected.name === name}
327
+ key={name}
328
+ label={name}
329
+ onClick={() => handleSelect({ type: "table", name })}
330
+ />
331
+ ))}
332
+ </SidebarSection>
333
+ )}
334
+
335
+ {hasViews && (
336
+ <SidebarSection
337
+ collapsed={collapsed.Views ?? false}
338
+ count={filteredViews.length}
339
+ label="Views"
340
+ onToggle={() => toggleSection("Views")}
341
+ >
342
+ {filteredViews.map((name) => (
343
+ <SidebarItem
344
+ isActive={selected?.type === "view" && selected.name === name}
345
+ key={name}
346
+ label={name}
347
+ onClick={() => handleSelect({ type: "view", name })}
348
+ />
349
+ ))}
350
+ </SidebarSection>
351
+ )}
352
+
353
+ {hasEnums && (
354
+ <SidebarSection
355
+ collapsed={collapsed.Enums ?? false}
356
+ count={filteredEnums.length}
357
+ label="Enums"
358
+ onToggle={() => toggleSection("Enums")}
359
+ >
360
+ {filteredEnums.map((name) => (
361
+ <SidebarItem
362
+ isActive={selected?.type === "enum" && selected.name === name}
363
+ key={name}
364
+ label={name}
365
+ onClick={() => handleSelect({ type: "enum", name })}
366
+ />
367
+ ))}
368
+ </SidebarSection>
369
+ )}
370
+ </>
371
+ );
372
+
373
+ const ColumnsTable = ({
374
+ columns,
375
+ enumValues,
376
+ }: {
377
+ columns: {
378
+ name: string;
379
+ type: string;
380
+ rawType: string;
381
+ nullable: boolean;
382
+ enumName: string | null;
383
+ }[];
384
+ enumValues: (name: string) => string[] | undefined;
385
+ }) => {
386
+ if (columns.length === 0) {
387
+ return (
388
+ <p className="py-4 font-mono text-slate-500 text-xs">
389
+ no columns defined
390
+ </p>
391
+ );
392
+ }
393
+
394
+ return (
395
+ <div className="overflow-x-auto rounded-lg border border-slate-800">
396
+ <table className="w-full border-collapse font-mono text-xs">
397
+ <thead>
398
+ <tr className="border-slate-800 border-b bg-slate-900/80">
399
+ <Th>Column</Th>
400
+ <Th>Type</Th>
401
+ <Th>Attributes</Th>
402
+ </tr>
403
+ </thead>
404
+ <tbody>
405
+ {columns.map((col) => {
406
+ const enumName: string | null = col.enumName;
407
+ const isEnum = enumName !== null;
408
+ const isSerial = col.rawType === "never";
409
+ const hasEnumValues = isEnum
410
+ ? (enumValues(enumName) ?? [])
411
+ : undefined;
412
+
413
+ return (
414
+ <tr
415
+ className="border-slate-800/50 border-b transition-colors hover:bg-slate-800/30"
416
+ key={col.name}
417
+ >
418
+ <Td>{col.name}</Td>
419
+ <Td>
420
+ <span
421
+ className={
422
+ isEnum
423
+ ? "text-amber-300"
424
+ : isSerial
425
+ ? "text-purple-300"
426
+ : "text-cyan-300"
427
+ }
428
+ title={col.rawType}
429
+ >
430
+ {col.type}
431
+ </span>
432
+ </Td>
433
+ <Td>
434
+ <div className="flex flex-wrap gap-1">
435
+ {isSerial && <SerialBadge />}
436
+ {col.nullable && <NullableBadge />}
437
+ {isEnum && <EnumBadge values={hasEnumValues ?? []} />}
438
+ </div>
439
+ </Td>
440
+ </tr>
441
+ );
442
+ })}
443
+ </tbody>
444
+ </table>
445
+ </div>
446
+ );
447
+ };
448
+
449
+ const Th = ({ children }: { children: React.ReactNode }) => (
450
+ <th className="px-4 py-2.5 text-left font-semibold text-slate-400">
451
+ {children}
452
+ </th>
453
+ );
454
+
455
+ const Td = ({ children }: { children: React.ReactNode }) => (
456
+ <td className="px-4 py-2.5 text-slate-300">{children}</td>
457
+ );
458
+
459
+ const TableDetail = ({
460
+ children,
461
+ enumValues,
462
+ onFKClick,
463
+ relationships,
464
+ tableName,
465
+ }: {
466
+ children: React.ReactNode;
467
+ enumValues: (name: string) => string[] | undefined;
468
+ onFKClick: (tableName: string) => void;
469
+ relationships: {
470
+ column: string;
471
+ referencedTable: string;
472
+ referencedColumn: string;
473
+ foreignKeyName: string;
474
+ isOneToOne: boolean;
475
+ }[];
476
+ tableName: string;
477
+ }) => (
478
+ <div>
479
+ <div className="mb-6">
480
+ <h2 className="font-mono font-semibold text-lg text-slate-100">
481
+ {tableName}
482
+ </h2>
483
+ <p className="font-mono text-slate-500 text-xs">table</p>
484
+ </div>
485
+
486
+ <div className="mb-8">
487
+ <h3 className="mb-3 font-mono text-slate-500 text-xs uppercase tracking-wider">
488
+ Columns
489
+ </h3>
490
+ {children}
491
+ </div>
492
+
493
+ {relationships.length > 0 && (
494
+ <div>
495
+ <h3 className="mb-3 font-mono text-slate-500 text-xs uppercase tracking-wider">
496
+ Relationships
497
+ </h3>
498
+ <div className="space-y-1.5">
499
+ {relationships.map((rel) => (
500
+ <RelationshipRow
501
+ enumValues={enumValues}
502
+ isOneToOne={rel.isOneToOne}
503
+ key={rel.foreignKeyName}
504
+ onTableClick={onFKClick}
505
+ rel={rel}
506
+ />
507
+ ))}
508
+ </div>
509
+ </div>
510
+ )}
511
+ </div>
512
+ );
513
+
514
+ const RelationshipRow = ({
515
+ enumValues,
516
+ isOneToOne,
517
+ onTableClick,
518
+ rel,
519
+ }: {
520
+ enumValues: (name: string) => string[] | undefined;
521
+ isOneToOne: boolean;
522
+ onTableClick: (tableName: string) => void;
523
+ rel: {
524
+ column: string;
525
+ referencedTable: string;
526
+ referencedColumn: string;
527
+ foreignKeyName: string;
528
+ isOneToOne: boolean;
529
+ };
530
+ }) => {
531
+ const hasEnumOnRef = enumValues(rel.referencedTable) !== undefined;
532
+ return (
533
+ <div
534
+ className="group rounded-lg border border-slate-800 bg-slate-900/30 p-3 transition-colors hover:border-slate-700"
535
+ key={rel.foreignKeyName}
536
+ >
537
+ <div className="mb-1 flex items-center gap-2 font-mono text-xs">
538
+ <code className="text-cyan-300">{rel.column}</code>
539
+ <span className="text-slate-600">
540
+ {isOneToOne ? "\u2192" : "\u2192\u2192"}
541
+ </span>
542
+ <button
543
+ className="cursor-pointer text-brand transition-colors hover:text-brand/80"
544
+ onClick={() => onTableClick(rel.referencedTable)}
545
+ title={`Jump to ${rel.referencedTable}`}
546
+ type="button"
547
+ >
548
+ {rel.referencedTable}
549
+ </button>
550
+ <code className="text-slate-500">.{rel.referencedColumn}</code>
551
+ {isOneToOne && (
552
+ <span className="rounded border border-purple-700/30 bg-purple-950/30 px-1.5 py-0.5 text-[10px] text-purple-300">
553
+ 1:1
554
+ </span>
555
+ )}
556
+ {hasEnumOnRef && (
557
+ <EnumBadge values={enumValues(rel.referencedTable) ?? []} />
558
+ )}
559
+ </div>
560
+ <div className="font-mono text-[10px] text-slate-600">
561
+ {rel.foreignKeyName.replace(/_fkey$/, "")}
562
+ </div>
563
+ </div>
564
+ );
565
+ };
566
+
567
+ const ViewDetail = ({
568
+ children,
569
+ viewName,
570
+ }: {
571
+ children: React.ReactNode;
572
+ viewName: string;
573
+ }) => (
574
+ <div>
575
+ <div className="mb-6">
576
+ <h2 className="font-mono font-semibold text-lg text-slate-100">
577
+ {viewName}
578
+ </h2>
579
+ <p className="font-mono text-slate-500 text-xs">view</p>
580
+ </div>
581
+
582
+ <div className="mb-8">
583
+ <h3 className="mb-3 font-mono text-slate-500 text-xs uppercase tracking-wider">
584
+ Columns
585
+ </h3>
586
+ {children}
587
+ </div>
588
+ </div>
589
+ );
590
+
591
+ const EnumDetail = ({ name, values }: { name: string; values: string[] }) => (
592
+ <div>
593
+ <div className="mb-6">
594
+ <h2 className="font-mono font-semibold text-lg text-slate-100">{name}</h2>
595
+ <p className="font-mono text-slate-500 text-xs">enum</p>
596
+ </div>
597
+
598
+ <div className="mb-8">
599
+ <h3 className="mb-3 font-mono text-slate-500 text-xs uppercase tracking-wider">
600
+ Values
601
+ </h3>
602
+ <div className="flex flex-wrap gap-2">
603
+ {values.map((v) => (
604
+ <span
605
+ className="rounded-lg border border-slate-700 bg-slate-900 px-3 py-1.5 font-mono text-slate-300 text-xs"
606
+ key={v}
607
+ >
608
+ {v}
609
+ </span>
610
+ ))}
611
+ </div>
612
+ </div>
613
+ </div>
614
+ );
615
+
616
+ export default SchemaViewer;
@@ -1381,6 +1381,30 @@ export type Database = {
1381
1381
  };
1382
1382
  Relationships: [];
1383
1383
  };
1384
+ messages_2026_08: {
1385
+ Row: {
1386
+ content: string;
1387
+ conversation_id: string;
1388
+ created_at: string;
1389
+ id: number;
1390
+ sender_id: string;
1391
+ };
1392
+ Insert: {
1393
+ content: string;
1394
+ conversation_id: string;
1395
+ created_at?: string;
1396
+ id?: number;
1397
+ sender_id: string;
1398
+ };
1399
+ Update: {
1400
+ content?: string;
1401
+ conversation_id?: string;
1402
+ created_at?: string;
1403
+ id?: number;
1404
+ sender_id?: string;
1405
+ };
1406
+ Relationships: [];
1407
+ };
1384
1408
  messages_default: {
1385
1409
  Row: {
1386
1410
  content: string;
package/src/vite-env.d.ts CHANGED
@@ -1 +1,36 @@
1
1
  /// <reference types="vite/client" />
2
+
3
+ declare module "virtual:supabase-schema" {
4
+ export type ColumnInfo = {
5
+ name: string;
6
+ type: string;
7
+ rawType: string;
8
+ nullable: boolean;
9
+ enumName: string | null;
10
+ };
11
+
12
+ export type RelationshipInfo = {
13
+ foreignKeyName: string;
14
+ column: string;
15
+ referencedTable: string;
16
+ referencedColumn: string;
17
+ isOneToOne: boolean;
18
+ };
19
+
20
+ export type TableInfo = {
21
+ columns: ColumnInfo[];
22
+ relationships: RelationshipInfo[];
23
+ };
24
+
25
+ export type ViewInfo = {
26
+ columns: ColumnInfo[];
27
+ };
28
+
29
+ export type Schema = {
30
+ tables: Record<string, TableInfo>;
31
+ views: Record<string, ViewInfo>;
32
+ enums: Record<string, string[]>;
33
+ };
34
+
35
+ export const schema: Schema;
36
+ }