@unciatech/file-manager 0.0.41 → 0.0.43

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/README.md CHANGED
@@ -1,383 +1,398 @@
1
1
  # 🗂️ File Manager
2
2
 
3
- A robust, production-ready file management component for any **React** application — works with Vite, Next.js, Remix, CRA, and more.
3
+ A robust, production-ready file management component for **React applications**.
4
4
 
5
- It supports deep folder nesting, drag-and-drop file uploads, metadata management for various file types (Images, Videos, Audio, Documents), unified grid layouts, and fully optimized loading states.
5
+ It works with **Vite, Next.js, Remix, CRA**, and other React frameworks.
6
6
 
7
- ## 🌟 Key Features
8
- - **Dual Operating Modes**: Use it as a standalone full-page media library or instantiate it as a picker modal for form inputs.
9
- - **Unified Grid View**: Beautiful, responsive layout that intelligently renders thumbnails, icons, and metadata based on the file's MIME type.
10
- - **Nested Folder Structure**: Infinite folder depth with smooth virtualized/paginated fetching.
11
- - **Provider Agnostic**: Built on an `IFileManagerProvider` interface. You can easily hot-swap the mock data provider for a real backend (Node.js, Supabase, Strapi, etc.).
12
- - **Bulk Actions**: Select multiple files/folders at once to bulk move or bulk delete.
13
- - **Optimistic UI Updates**: Instant visual feedback when renaming folders or updating file descriptions, with silent background synchronization.
14
- - **Graceful Error Handling**: Resilient `<FileManagerErrorBoundary>` that captures catastrophic failures and allows users to hard-reload safely without app crashes.
7
+ The library supports deep folder hierarchies, drag-and-drop uploads, metadata management for multiple file types (Images, Videos, Audio, Documents), and a responsive grid interface optimized for large media libraries.
15
8
 
16
- ## 🛠️ Tech Stack
17
- - **Framework**: React (any — Vite, Next.js, Remix, CRA)
18
- - **Styling**: Tailwind CSS
19
- - **Icons**: Custom SVG Icons
20
- - **Notifications**: Sonner
9
+ ---
10
+
11
+ # 🌟 Key Features
12
+
13
+ * **Dual Operating Modes** - Use as a full-page media library or as a picker modal.
14
+ * **Unified Grid View** - Responsive layout that intelligently renders thumbnails, icons, and metadata.
15
+ * **Nested Folder Structure** - Infinite folder depth with smooth paginated fetching.
16
+ * **Provider Agnostic** - Integrates with any backend through the `IFileManagerProvider` interface.
17
+ * **Bulk Actions** - Select and move/delete multiple files or folders.
18
+ * **Optimistic UI Updates** - Instant UI feedback with background synchronization.
19
+ * **Error Boundary Protection** - Built-in `<FileManagerErrorBoundary>` prevents crashes.
20
+
21
+ ---
22
+
23
+ # 📦 Installation
24
+
25
+ Install the file manager and required utilities:
26
+
27
+ ```bash
28
+ npm install @unciatech/file-manager class-variance-authority clsx tailwind-merge
29
+ ```
30
+
31
+ > ⚠️ This library relies on **Tailwind CSS** for styling.
32
+ > If your project does not use Tailwind yet, install it in Step 3.
33
+
34
+ ---
35
+
36
+ # ⚡ Quick Start
37
+
38
+ You can instantly test the UI using the built-in `MockProvider`.
39
+
40
+ ```tsx
41
+ import { FileManager, MockProvider } from "@unciatech/file-manager"
42
+
43
+ const provider = new MockProvider()
44
+
45
+ export default function App() {
46
+ return (
47
+ <FileManager
48
+ provider={provider}
49
+ basePath="/"
50
+ viewMode="grid"
51
+ allowedFileTypes={["images", "videos", "files"]}
52
+ />
53
+ )
54
+ }
55
+ ```
56
+
57
+ ---
58
+
59
+ # 🛠 Setup & Requirements
60
+
61
+ ## Step 1 - Install the Library
62
+
63
+ ```bash
64
+ npm install @unciatech/file-manager class-variance-authority clsx tailwind-merge
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Step 2 - Import Styles
70
+
71
+ Import the file manager styles in your root file.
72
+
73
+ ### Vite / React
74
+
75
+ ```ts
76
+ import "@unciatech/file-manager/styles"
77
+ ```
78
+
79
+ Place this inside:
80
+
81
+ ```
82
+ main.tsx or App.tsx
83
+ ```
84
+
85
+ ### Next.js
86
+
87
+ Import inside your root layout:
88
+
89
+ ```ts
90
+ import "@unciatech/file-manager/styles"
91
+ ```
92
+
93
+ Place this in:
94
+
95
+ ```
96
+ app/layout.tsx
97
+ ```
98
+
99
+ ---
100
+
101
+ # Step 3 - Configure Tailwind CSS
102
+
103
+ This library relies heavily on **Tailwind CSS**.
21
104
 
22
- > [!WARNING]
23
- > This library is currently in **BETA**. Please report any bugs or feature requests on the GitHub issues page.
105
+ If your project **already uses Tailwind**, follow Option A.
106
+ If **not**, follow Option B.
24
107
 
25
- ## Setup & Requirements
108
+ ---
26
109
 
27
- Because this library relies heavily on Tailwind CSS for minimal zero-config styling, ensure your app is configured to scan the library's components for Tailwind utility classes.
110
+ ## Option A - Project Already Uses Tailwind
28
111
 
29
112
  ### Tailwind v4
30
- If you are using **Tailwind CSS v4** (like in newer Next.js or Vite projects), add this to your main CSS file (`globals.css` or `index.css`). This pulls in the required theme configuration and ensures the library is scanned:
113
+
114
+ Add this to your main CSS file (`globals.css` or `index.css`):
31
115
 
32
116
  ```css
33
117
  @import "tailwindcss";
34
118
  @import "@unciatech/file-manager/styles";
119
+
35
120
  @source "../node_modules/@unciatech/file-manager";
36
121
  ```
37
122
 
123
+ ---
124
+
38
125
  ### Tailwind v3
39
- If you are still on **Tailwind v3**, add the library path to your `tailwind.config.ts`:
126
+
127
+ Update your `tailwind.config.ts`:
40
128
 
41
129
  ```ts
42
130
  content: [
43
- // ... your other paths
44
131
  "./node_modules/@unciatech/file-manager/dist/**/*.{js,ts,jsx,tsx}",
45
- ],
132
+ ]
46
133
  ```
47
134
 
48
- ## Basic Usage in Your Project
49
-
50
- If you want to integrate this File Manager into your own React application (Next.js, Vite, Remix, CRA, etc.), follow this step-by-step guide.
135
+ ---
51
136
 
52
- ### Step 1: Install the Package
137
+ ## Option B - Install Tailwind CSS
53
138
 
54
- Install the library via NPM:
55
139
  ```bash
56
- npm install @unciatech/file-manager
140
+ npm install -D tailwindcss postcss autoprefixer
141
+ npx tailwindcss init -p
57
142
  ```
58
143
 
59
- **(Optional) ⚡ Magic Quick Start Scaffolding**
60
- Instead of setting everything up manually, our init script can spawn a brand new full-stack application instantly:
61
- ```bash
62
- npx @unciatech/file-manager init my-media-app
63
- ```
64
- *It will ask if you want Next.js or Vite (React), install Tailwind, install the package, and set everything up including styles!*
144
+ Update `tailwind.config.ts`:
65
145
 
66
- **(CRITICAL) Import the styles:**
67
- The init script includes this automatically, but if you are installing manually, add this import to your root layout / entry file:
68
146
  ```ts
69
- import '@unciatech/file-manager/styles';
147
+ content: [
148
+ "./index.html",
149
+ "./src/**/*.{js,ts,jsx,tsx}",
150
+ "./node_modules/@unciatech/file-manager/dist/**/*.{js,ts,jsx,tsx}",
151
+ ]
70
152
  ```
71
153
 
72
- ### Step 2: Create your Custom API Provider
154
+ Add to `index.css` or `globals.css`:
73
155
 
74
- The file manager is completely agnostic to your backend database. You simply need to create a class that implements the `IFileManagerProvider` interface.
156
+ ```css
157
+ @tailwind base;
158
+ @tailwind components;
159
+ @tailwind utilities;
75
160
 
76
- Here is an example of what your custom provider might look like, making real API calls to your backend using `fetch`:
161
+ @import "@unciatech/file-manager/styles";
162
+ ```
77
163
 
78
- ```typescript
79
- // lib/my-api-provider.ts
80
- import {
81
- IFileManagerProvider,
82
- FolderId,
83
- FileUploadInput
84
- } from "@/types/file-manager"; // Or "@unciatech/file-manager" for external users
164
+ ---
85
165
 
86
- export class MyCustomApiProvider implements IFileManagerProvider {
87
- private baseUrl = "https://api.mybackend.com/v1";
166
+ # Step 4 - Create Your Custom API Provider
88
167
 
89
- // Example 1: Fetching folders from your Real API
90
- async getFolders(folderId: FolderId, page = 1, limit = 24) {
91
- const parentQuery = folderId ? `&parentId=${folderId}` : "&isRoot=true";
92
-
93
- // Simulate real API Call
94
- const res = await fetch(`${this.baseUrl}/folders?page=${page}&limit=${limit}${parentQuery}`);
95
-
96
- if (!res.ok) throw new Error("Failed to fetch folders");
97
-
98
- const data = await res.json();
99
-
100
- return {
101
- folders: data.folders, // Array of Folder objects matching our interface
102
- pagination: data.pagination // { currentPage, totalPages, totalFiles, filesPerPage }
103
- };
104
- }
168
+ > 💡 **Pro Tip - The Mock Provider**
169
+ > If you are just prototyping and don't have a backend ready yet, you can skip this step entirely.
170
+ >
171
+ > The library includes a fully functional `MockProvider` that simulates network latency and stores data in memory.
105
172
 
106
- // Example 2: Uploading files via multipart/form-data
107
- async uploadFiles(filesInput: FileUploadInput[], folderId?: FolderId) {
108
- const formData = new FormData();
109
- if (folderId) formData.append("folderId", String(folderId));
110
-
111
- filesInput.forEach(({ file }) => {
112
- formData.append("files", file);
113
- });
173
+ Example:
114
174
 
115
- const res = await fetch(`${this.baseUrl}/upload`, {
116
- method: 'POST',
117
- body: formData
118
- });
175
+ ```tsx
176
+ import { FileManager, MockProvider } from "@unciatech/file-manager"
119
177
 
120
- return res.json(); // Returns array of FileMetaData
121
- }
178
+ const provider = new MockProvider()
122
179
 
123
- // ... Implement the remaining interface methods (getFiles, renameFolder, bulkMove, etc.)
124
- }
180
+ <FileManager provider={provider} basePath="/" viewMode="grid" />
125
181
  ```
126
182
 
127
- > **💡 Pro Tip - The Mock Provider:**
128
- > If you are just prototyping and don't have a backend ready yet, you can skip Step 2 entirely! We included a fully functional `MockProvider` that fakes network latency and stores data in memory. Just import it and use it right away to see the UI in action.
183
+ ---
129
184
 
130
- // app/media/page.tsx
131
- import { FileManager, MockProvider } from "@unciatech/file-manager";
132
- import "@unciatech/file-manager/styles";
185
+ ### Using Your Own Backend
133
186
 
134
- // **Tailwind v4 Users:** Make sure your `globals.css` or `index.css` includes:
135
- // @source "../node_modules/@unciatech/file-manager";
136
- // OR import your real provider
137
- import { MyCustomApiProvider } from "@/lib/my-api-provider";
187
+ The file manager is **backend-agnostic**.
138
188
 
139
- export default function MediaLibraryPage() {
140
- // Instantiate the provider (Real or Mock)
141
- const apiProvider = new MockProvider();
189
+ Create a class implementing `IFileManagerProvider`.
142
190
 
143
- return (
144
- <div className="h-screen w-full">
145
- <FileManagerProvider
146
- mode="page"
147
- selectionMode="multiple"
148
- allowedFileTypes={["images", "videos", "audios", "files"]}
149
- provider={apiProvider}
150
- >
151
- <FileManager basePath="/media" />
152
- </FileManagerProvider>
153
- </div>
154
- );
155
- }
156
- ```
191
+ ```ts
192
+ import {
193
+ IFileManagerProvider,
194
+ FolderId,
195
+ FileUploadInput,
196
+ } from "@unciatech/file-manager"
157
197
 
158
- ---
198
+ export class MyCustomApiProvider implements IFileManagerProvider {
199
+ private baseUrl = "https://api.mybackend.com/v1"
159
200
 
160
- ## 🔀 Framework Router Integration
201
+ async getFolders(folderId: FolderId, page = 1, limit = 24) {
202
+ const parentQuery = folderId ? `&parentId=${folderId}` : "&isRoot=true"
161
203
 
162
- By default, the file manager uses the browser's native `history.pushState` API for navigation — no full page reloads, no dependencies. This works out of the box in any bare React app (Vite, CRA, etc.).
204
+ const res = await fetch(
205
+ `${this.baseUrl}/folders?page=${page}&limit=${limit}${parentQuery}`
206
+ )
163
207
 
164
- However, if your app already has its own router (React Router, Next.js, TanStack Router), you should pass the `onNavigate` prop so the file manager delegates all navigation to your router instead of calling `history.pushState` directly. This keeps your router's internal state in sync.
208
+ if (!res.ok) throw new Error("Failed to fetch folders")
165
209
 
166
- ### React Router v6
210
+ const data = await res.json()
167
211
 
168
- ```tsx
169
- import { useNavigate } from 'react-router-dom';
212
+ return {
213
+ folders: data.folders,
214
+ pagination: data.pagination,
215
+ }
216
+ }
217
+
218
+ async uploadFiles(filesInput: FileUploadInput[], folderId?: FolderId) {
219
+ const formData = new FormData()
170
220
 
171
- function MyPage() {
172
- const navigate = useNavigate();
221
+ if (folderId) {
222
+ formData.append("folderId", String(folderId))
223
+ }
173
224
 
174
- return (
175
- <FileManager
176
- provider={provider}
177
- allowedFileTypes={['images', 'videos']}
178
- onNavigate={(url) => navigate(url)}
179
- basePath="/media"
180
- />
181
- );
225
+ filesInput.forEach(({ file }) => {
226
+ formData.append("files", file)
227
+ })
228
+
229
+ const res = await fetch(`${this.baseUrl}/upload`, {
230
+ method: "POST",
231
+ body: formData,
232
+ })
233
+
234
+ return res.json()
235
+ }
182
236
  }
183
237
  ```
184
238
 
185
- ### Next.js (App Router)
239
+ ---
186
240
 
187
- ```tsx
188
- 'use client';
189
- import { useRouter } from 'next/navigation';
241
+ # 🔀 Router Integration
190
242
 
191
- export default function MediaPage() {
192
- const router = useRouter();
243
+ By default the file manager uses `history.pushState`.
193
244
 
194
- return (
195
- <FileManager
196
- provider={provider}
197
- allowedFileTypes={['images', 'videos']}
198
- onNavigate={(url) => router.push(url)}
199
- basePath="/media"
200
- />
201
- );
202
- }
203
- ```
245
+ If your app uses a router, pass the `onNavigate` prop.
204
246
 
205
- ### TanStack Router
247
+ ---
206
248
 
207
- ```tsx
208
- import { useRouter } from '@tanstack/react-router';
249
+ # Example - React Router
209
250
 
210
- function MyPage() {
211
- const router = useRouter();
251
+ ### Install
212
252
 
213
- return (
214
- <FileManager
215
- provider={provider}
216
- allowedFileTypes={['images', 'videos']}
217
- onNavigate={(url) => router.navigate({ href: url })}
218
- basePath="/media"
219
- />
220
- );
221
- }
253
+ ```bash
254
+ npm install react-router-dom
222
255
  ```
223
256
 
257
+ ### main.tsx
258
+
224
259
  ```tsx
225
- <FileManager
226
- provider={provider}
227
- allowedFileTypes={['images', 'videos']}
228
- basePath="/media"
229
- // no onNavigate needed
230
- />
260
+ import React from "react"
261
+ import ReactDOM from "react-dom/client"
262
+ import { BrowserRouter } from "react-router-dom"
263
+ import App from "./App"
264
+ import "./index.css"
265
+
266
+ ReactDOM.createRoot(document.getElementById("root")!).render(
267
+ <React.StrictMode>
268
+ <BrowserRouter>
269
+ <App />
270
+ </BrowserRouter>
271
+ </React.StrictMode>
272
+ )
231
273
  ```
232
274
 
233
- ## 🎮 Playgrounds & Reference Implementations
275
+ ---
234
276
 
235
- For complete, working examples of how to integrate the File Manager into different application architectures, check out the [playgrounds](file:///Users/avi/Developer/Uncia/file-manager/playgrounds) directory. These are fully functional Vite-based projects that demonstrate real-world integration patterns.
277
+ # Example - TanStack Router
236
278
 
237
- - **[React Router v7 Playground](file:///Users/avi/Developer/Uncia/file-manager/playgrounds/test-react-router)**: Demonstrates integration with `react-router-dom` using `useNavigate` and route-based navigation.
238
- - **[TanStack Router Playground](file:///Users/avi/Developer/Uncia/file-manager/playgrounds/test-tanstack)**: Demonstrates integration with `@tanstack/react-router` using the `router` object and `href` based navigation.
279
+ ### Install
239
280
 
240
- These playgrounds are a great starting point for seeing how to handle:
241
- - Styling with Tailwind CSS v4
242
- - Mapping `onNavigate` to your framework's router
243
- - Using the `MockProvider` for rapid prototyping
244
- - Configuring `FileManagerModal` v/s full-page `FileManager`
281
+ ```bash
282
+ npm install @tanstack/react-router
283
+ ```
245
284
 
246
- ---
285
+ ### main.tsx
247
286
 
248
- > [!NOTE]
249
- > The `onNavigate` prop is also available on `<FileManagerModal>` for modal mode.
287
+ ```tsx
288
+ import React from "react"
289
+ import ReactDOM from "react-dom/client"
290
+ import {
291
+ RouterProvider,
292
+ createRouter,
293
+ createRoute,
294
+ createRootRoute,
295
+ } from "@tanstack/react-router"
296
+
297
+ import App, { ModalPage } from "./App"
298
+ import "./index.css"
299
+
300
+ const rootRoute = createRootRoute()
301
+
302
+ const indexRoute = createRoute({
303
+ getParentRoute: () => rootRoute,
304
+ path: "/",
305
+ component: ModalPage,
306
+ })
307
+
308
+ const fullRoute = createRoute({
309
+ getParentRoute: () => rootRoute,
310
+ path: "/full",
311
+ component: App,
312
+ })
313
+
314
+ const catchAllRoute = createRoute({
315
+ getParentRoute: () => rootRoute,
316
+ path: "$",
317
+ component: App,
318
+ })
319
+
320
+ const routeTree = rootRoute.addChildren([
321
+ indexRoute,
322
+ fullRoute,
323
+ catchAllRoute,
324
+ ])
325
+
326
+ const router = createRouter({ routeTree })
327
+
328
+ declare module "@tanstack/react-router" {
329
+ interface Register {
330
+ router: typeof router
331
+ }
332
+ }
250
333
 
251
- ---
334
+ ReactDOM.createRoot(document.getElementById("root")!).render(
335
+ <React.StrictMode>
336
+ <RouterProvider router={router} />
337
+ </React.StrictMode>
338
+ )
339
+ ```
252
340
 
253
- ## 💾 Database Schema Design
341
+ ---
254
342
 
255
- Because this application relies heavily on tree structures (Folders inside Folders) and varied JSON metadata (Video durations vs Document page counts), using a relational database with JSONB support (like PostgreSQL) is highly recommended.
343
+ # 🗄️ Database Schema (Optional)
256
344
 
257
- Below are production-ready schema examples using **Prisma** and **Drizzle ORM**.
345
+ Using **PostgreSQL** with JSON support is recommended.
258
346
 
259
- ### Option A: Prisma Schema (PostgreSQL/MySQL)
347
+ Example Prisma schema:
260
348
 
261
349
  ```prisma
262
- generator client {
263
- provider = "prisma-client-js"
264
- }
265
-
266
- datasource db {
267
- provider = "postgresql" // or "mysql"
268
- url = env("DATABASE_URL")
269
- }
270
-
271
350
  model Folder {
272
- id Int @id @default(autoincrement())
351
+ id Int @id @default(autoincrement())
273
352
  name String
274
-
275
- // Self-referencing relationship for infinite folder depth
276
- parentId Int?
277
- parent Folder? @relation("FolderHierarchy", fields: [parentId], references: [id])
278
- children Folder[] @relation("FolderHierarchy")
279
-
280
- // Cached counts for fast UI rendering
281
- folderCount Int @default(0)
282
- fileCount Int @default(0)
283
-
284
- // Path optimization
285
- pathId Int @default(0)
286
- path String? // e.g. "/1/5/12"
287
-
288
- createdAt DateTime @default(now())
289
- updatedAt DateTime @updatedAt
290
-
353
+ parentId Int?
354
+
355
+ folderCount Int @default(0)
356
+ fileCount Int @default(0)
357
+
358
+ pathId Int @default(0)
359
+ path String?
360
+
361
+ createdAt DateTime @default(now())
362
+ updatedAt DateTime @updatedAt
363
+
291
364
  files File[]
292
365
 
293
366
  @@index([parentId])
294
367
  }
295
368
 
296
369
  model File {
297
- id Int @id @default(autoincrement())
298
- name String
299
-
300
- // Foreign Key to Folder
301
- folderId Int?
302
- folder Folder? @relation(fields: [folderId], references: [id], onDelete: Cascade)
303
- folderPath String?
304
-
305
- // Core Asset Data
306
- size Int
307
- url String
308
- previewUrl String? // Lightweight thumbnail URL
309
- mime String // e.g. "image/jpeg", "video/mp4"
310
- ext String?
311
- hash String?
312
-
313
- // Common Media Details
314
- alternativeText String?
315
- caption String?
316
- width Int?
317
- height Int?
318
-
319
- // JSONB storage for flexible metadata
320
- // (e.g., formats: { thumbnail: {...}, small: {...} })
321
- formats Json?
322
- // (e.g., metaData: { duration: 120, bitrate: 320, pageCount: 15 })
323
- metaData Json?
324
-
325
- createdAt DateTime @default(now())
326
- updatedAt DateTime @updatedAt
327
-
328
- @@index([folderId])
370
+ id Int @id @default(autoincrement())
371
+ name String
372
+ folderId Int?
373
+
374
+ size Int
375
+ url String
376
+ mime String
377
+
378
+ formats Json?
379
+ metaData Json?
380
+
381
+ createdAt DateTime @default(now())
382
+ updatedAt DateTime @updatedAt
329
383
  }
330
384
  ```
331
385
 
386
+ ---
332
387
 
333
- ### Option B: Drizzle ORM Schema (PostgreSQL)
334
-
335
- If you prefer a lighter, TypeScript-first approach using Drizzle:
336
-
337
- ```typescript
338
- import { pgTable, serial, varchar, integer, timestamp, jsonb, text } from "drizzle-orm/pg-core";
339
-
340
- export const folders = pgTable("folders", {
341
- id: serial("id").primaryKey(),
342
- name: varchar("name", { length: 255 }).notNull(),
343
-
344
- // Nullable parentId allows root-level folders
345
- parentId: integer("parent_id"), // Needs recursive FK setup in relations
346
-
347
- folderCount: integer("folder_count").default(0),
348
- fileCount: integer("file_count").default(0),
349
-
350
- pathId: integer("path_id").default(0),
351
- path: varchar("path", { length: 255 }),
352
-
353
- createdAt: timestamp("created_at").defaultNow().notNull(),
354
- updatedAt: timestamp("updated_at").defaultNow().notNull(),
355
- });
356
-
357
- export const files = pgTable("files", {
358
- id: serial("id").primaryKey(),
359
- name: varchar("name", { length: 255 }).notNull(),
360
-
361
- folderId: integer("folder_id").references(() => folders.id, { onDelete: "cascade" }),
362
- folderPath: varchar("folder_path", { length: 255 }),
363
-
364
- size: integer("size").notNull(),
365
- url: text("url").notNull(),
366
- previewUrl: text("preview_url"),
367
- mime: varchar("mime", { length: 100 }).notNull(),
368
- ext: varchar("ext", { length: 20 }),
369
- hash: varchar("hash", { length: 255 }),
370
-
371
- alternativeText: text("alternative_text"),
372
- caption: text("caption"),
373
- width: integer("width"),
374
- height: integer("height"),
375
-
376
- // JSONB is perfect for storing our dynamic metadata & responsive formats
377
- formats: jsonb("formats"),
378
- metaData: jsonb("meta_data"),
379
-
380
- createdAt: timestamp("created_at").defaultNow().notNull(),
381
- updatedAt: timestamp("updated_at").defaultNow().notNull(),
382
- });
383
- ```
388
+ # ⚠️ Status
389
+
390
+ > This library is currently in **BETA**.
391
+
392
+ Please report bugs or feature requests through **GitHub Issues**.
393
+
394
+ ---
395
+
396
+ # License
397
+
398
+ MIT