@neevjs/client 0.0.1 → 1.0.1-beta

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rahul7raj
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NeevClientConfig, NeevClientInterface, AuthInterface, AuthUser, ModelRecord, UseModelReturn, SyncStatus, NeevPlugin } from '@neevjs/shared';
2
- export { ApiMeta, ApiResponse, AuthInterface, AuthLoginResponse, AuthUser, ModelRecord, NeevClientConfig, NeevClientInterface, NeevPlugin, NeevRequest, Pagination, QueuedAction, RequestOptions, SyncStatus, UseModelReturn } from '@neevjs/shared';
3
- import React from 'react';
2
+ export { ApiMeta, ApiResponse, AuthInterface, AuthLoginResponse, AuthUser, ModelRecord, NeevClientConfig, NeevClientInterface, NeevPlugin, NeevRequest, Pagination, QueuedAction, RequestOptions, SyncStatus, UseModelReturn, VERSION } from '@neevjs/shared';
3
+ import React, { ReactNode } from 'react';
4
4
 
5
5
  declare function createClient(config?: NeevClientConfig): NeevClientInterface;
6
6
 
@@ -24,25 +24,175 @@ declare class AuthClient implements AuthInterface {
24
24
  getToken(): string | null;
25
25
  }
26
26
 
27
- declare function useModel<T extends ModelRecord>(name: string): UseModelReturn<T>;
27
+ /**
28
+ * SecureStore — AES-256-GCM encrypted client-side storage for NeevJS.
29
+ *
30
+ * ## Security model
31
+ * - Data is encrypted with AES-256-GCM before being written to localStorage.
32
+ * - The encryption key is generated fresh every session and lives ONLY in memory.
33
+ * - On page refresh the key is gone, so the encrypted ciphertext in localStorage
34
+ * is permanently unreadable — it is automatically discarded.
35
+ * - This means SecureStore does NOT persist across page refreshes by design.
36
+ * If you need truly sensitive data to survive refreshes, store it on the server.
37
+ *
38
+ * ## Threat model
39
+ * ✅ Protects against: physical access to device / localStorage dump
40
+ * ✅ Protects against: another browser tab/window reading the storage
41
+ * ❌ Does NOT protect against: XSS (attacker JS runs in your origin and can
42
+ * call SecureStore.get() directly — same as any in-memory value)
43
+ *
44
+ * ## When to use
45
+ * - Temporary sensitive values during a session (payment amount, masked card, OTP state)
46
+ * - Values that must not appear in plaintext in DevTools Application → Storage
47
+ *
48
+ * ## API
49
+ * ```ts
50
+ * await SecureStore.set('pending_payment', { amount: 4999, currency: 'INR' })
51
+ * const payment = await SecureStore.get<Payment>('pending_payment')
52
+ * SecureStore.remove('pending_payment')
53
+ * SecureStore.clear() // remove all SecureStore entries
54
+ * ```
55
+ */
56
+ declare const SecureStore: {
57
+ /**
58
+ * Encrypt and store a value in localStorage.
59
+ * The value cannot be read without the in-memory session key.
60
+ */
61
+ set<T>(key: string, value: T): Promise<void>;
62
+ /**
63
+ * Retrieve and decrypt a value.
64
+ * Returns null if the key doesn't exist or cannot be decrypted
65
+ * (e.g. after a page refresh when the key is gone).
66
+ */
67
+ get<T>(key: string): Promise<T | null>;
68
+ /**
69
+ * Remove a specific key from SecureStore.
70
+ */
71
+ remove(key: string): void;
72
+ /**
73
+ * Remove ALL SecureStore entries from localStorage.
74
+ * Call this on logout to clean up all session-bound encrypted data.
75
+ */
76
+ clear(): void;
77
+ };
78
+
79
+ interface UseModelOptions {
80
+ suspense?: boolean;
81
+ /**
82
+ * Query parameters appended to the request URL.
83
+ * The full URL (including params) is used as the cache key, so
84
+ * useModel('users', { params: { page: 1 } }) and
85
+ * useModel('users', { params: { page: 2 } }) cache independently.
86
+ *
87
+ * @example params: { page: 1, role: 'admin', search: 'rahul' }
88
+ */
89
+ params?: Record<string, string | number | boolean | undefined>;
90
+ /**
91
+ * Override the client's default baseURL for this specific model instance.
92
+ * Useful for Hybrid Mode (connecting to different backends).
93
+ */
94
+ baseURL?: string;
95
+ }
96
+ declare function useModel<T extends ModelRecord>(name: string, options?: UseModelOptions): UseModelReturn<T>;
28
97
 
29
98
  declare function useAuth(): AuthInterface;
30
99
 
31
100
  declare function useSyncStatus(): SyncStatus;
32
101
 
102
+ interface UseStoreOptions {
103
+ /**
104
+ * Persist value to localStorage. Survives page reloads and browser restarts.
105
+ *
106
+ * ⚠️ SECURITY: localStorage is readable by any JavaScript on the page.
107
+ * Do NOT use for sensitive data (passwords, payment info, PII).
108
+ * Use `{ session: true }` instead for semi-sensitive data.
109
+ *
110
+ * @default false
111
+ */
112
+ persist?: boolean;
113
+ /**
114
+ * Store value in sessionStorage. Survives page refresh but is:
115
+ * - Cleared when the browser tab closes
116
+ * - NOT shared across tabs
117
+ * - Safer than localStorage for semi-sensitive data (e.g. current user preferences)
118
+ *
119
+ * Cannot be combined with `persist: true`.
120
+ *
121
+ * @default false
122
+ */
123
+ session?: boolean;
124
+ /**
125
+ * TTL (time-to-live) in milliseconds. After this duration, the stored value
126
+ * expires and falls back to `initialValue`.
127
+ * Only applies when `persist` or `session` is true.
128
+ *
129
+ * @example ttl: 7 * 24 * 60 * 60 * 1000 // 7 days
130
+ */
131
+ ttl?: number;
132
+ }
133
+ /**
134
+ * `useStore` — Global client-side state management for NeevJS.
135
+ *
136
+ * Built on React 18's `useSyncExternalStore` for concurrent-mode safety.
137
+ *
138
+ * Security levels:
139
+ * - Default (in-memory only) → Most secure. Lost on refresh.
140
+ * - `{ session: true }` → sessionStorage. Safe for semi-sensitive data.
141
+ * - `{ persist: true }` → localStorage. For non-sensitive preferences only.
142
+ *
143
+ * For truly sensitive data that must persist across sessions → store on the server.
144
+ */
145
+ declare function useStore<T>(key: string, initialValue: T, options?: UseStoreOptions): [T, (updater: T | ((prev: T) => T)) => void];
146
+ declare function getStore<T>(key: string): T | undefined;
147
+ declare function setStore<T>(key: string, value: T): void;
148
+ declare function clearPersistedStore(key: string): void;
149
+
33
150
  interface TableColumn<T extends ModelRecord> {
34
151
  key: keyof T & string;
35
152
  label?: string;
36
153
  render?: (value: T[keyof T], row: T) => React.ReactNode;
37
154
  }
155
+ interface TableClassNames {
156
+ root?: string;
157
+ table?: string;
158
+ thead?: string;
159
+ tr?: string;
160
+ th?: string;
161
+ tbody?: string;
162
+ td?: string;
163
+ actions?: string;
164
+ editButton?: string;
165
+ deleteButton?: string;
166
+ emptyState?: string;
167
+ errorState?: string;
168
+ loadingState?: string;
169
+ }
170
+ interface TableStyles {
171
+ root?: React.CSSProperties;
172
+ table?: React.CSSProperties;
173
+ thead?: React.CSSProperties;
174
+ tr?: React.CSSProperties;
175
+ th?: React.CSSProperties;
176
+ tbody?: React.CSSProperties;
177
+ td?: React.CSSProperties;
178
+ actions?: React.CSSProperties;
179
+ editButton?: React.CSSProperties;
180
+ deleteButton?: React.CSSProperties;
181
+ emptyState?: React.CSSProperties;
182
+ errorState?: React.CSSProperties;
183
+ loadingState?: React.CSSProperties;
184
+ }
38
185
  interface TableProps<T extends ModelRecord> {
39
186
  model: string;
40
187
  columns?: TableColumn<T>[];
41
188
  onEdit?: (row: T) => void;
42
189
  onDelete?: (row: T) => void;
43
190
  emptyMessage?: string;
191
+ classNames?: TableClassNames;
192
+ styles?: TableStyles;
193
+ unstyled?: boolean;
44
194
  }
45
- declare function Table<T extends ModelRecord>({ model, columns, onEdit, onDelete, emptyMessage, }: TableProps<T>): React.ReactElement;
195
+ declare function Table<T extends ModelRecord>({ model, columns, onEdit, onDelete, emptyMessage, classNames, styles, unstyled, }: TableProps<T>): React.ReactElement;
46
196
 
47
197
  interface FormField {
48
198
  name: string;
@@ -55,6 +205,26 @@ interface FormField {
55
205
  value: string;
56
206
  }[];
57
207
  }
208
+ interface FormClassNames {
209
+ root?: string;
210
+ error?: string;
211
+ fieldError?: string;
212
+ label?: string;
213
+ input?: string;
214
+ select?: string;
215
+ textarea?: string;
216
+ submitButton?: string;
217
+ }
218
+ interface FormStyles {
219
+ root?: React.CSSProperties;
220
+ error?: React.CSSProperties;
221
+ fieldError?: React.CSSProperties;
222
+ label?: React.CSSProperties;
223
+ input?: React.CSSProperties;
224
+ select?: React.CSSProperties;
225
+ textarea?: React.CSSProperties;
226
+ submitButton?: React.CSSProperties;
227
+ }
58
228
  interface FormProps<T extends ModelRecord> {
59
229
  model: string;
60
230
  fields?: FormField[];
@@ -63,16 +233,44 @@ interface FormProps<T extends ModelRecord> {
63
233
  onSuccess?: () => void;
64
234
  onError?: (err: Error) => void;
65
235
  submitLabel?: string;
236
+ classNames?: FormClassNames;
237
+ styles?: FormStyles;
238
+ unstyled?: boolean;
239
+ transformPayload?: (payload: Partial<T>) => Partial<T> | Promise<Partial<T>>;
240
+ onSubmitOverride?: (payload: Partial<T>) => Promise<void>;
66
241
  children?: React.ReactNode;
242
+ /**
243
+ * Per-field validation errors. Keys are field names, values are error messages.
244
+ * Use this to display server-side validation errors next to the relevant field.
245
+ * @example fieldErrors={{ email: 'Email is already taken' }}
246
+ */
247
+ fieldErrors?: Record<string, string>;
248
+ /**
249
+ * Client-side validation function. Called before submission.
250
+ * Return an object of field errors to block submission, or undefined/null to proceed.
251
+ * @example validate={(values) => values.password.length < 8 ? { password: 'Min 8 characters' } : undefined}
252
+ */
253
+ validate?: (values: Partial<T>) => Record<string, string> | undefined | null;
67
254
  }
68
- declare function Form<T extends ModelRecord>({ model, fields, initialValues, editId, onSuccess, onError, submitLabel, children, }: FormProps<T>): React.ReactElement;
255
+ declare function Form<T extends ModelRecord>({ model, fields, initialValues, editId, onSuccess, onError, submitLabel, classNames, styles, unstyled, transformPayload, onSubmitOverride, children, fieldErrors: externalFieldErrors, validate, }: FormProps<T>): React.ReactElement;
69
256
 
70
257
  interface ProtectedProps {
71
258
  children: React.ReactNode;
259
+ /** Shown while auth state is being verified. Defaults to null (invisible). */
260
+ loadingFallback?: React.ReactNode;
261
+ /** Shown when user is not authenticated or lacks the required role. */
72
262
  fallback?: React.ReactNode;
263
+ /** If provided, user must have this exact role string to access the content. */
73
264
  role?: string;
74
265
  }
75
- declare function Protected({ children, fallback, role }: ProtectedProps): React.ReactElement;
266
+ declare function Protected({ children, loadingFallback, fallback, role }: ProtectedProps): React.ReactElement;
267
+
268
+ interface NeevBoundaryProps {
269
+ children: ReactNode;
270
+ loadingFallback?: ReactNode;
271
+ errorFallback?: (error: Error, resetErrorBoundary: () => void) => ReactNode;
272
+ }
273
+ declare function NeevBoundary({ children, loadingFallback, errorFallback }: NeevBoundaryProps): React.ReactElement;
76
274
 
77
275
  declare const AuthPlugin: NeevPlugin;
78
276
 
@@ -88,4 +286,4 @@ declare const CachePlugin: NeevPlugin;
88
286
  declare function createOfflinePlugin(): NeevPlugin;
89
287
  declare const OfflinePlugin: NeevPlugin;
90
288
 
91
- export { AuthClient, AuthPlugin, CachePlugin, Form, LoggerPlugin, NeevContext, NeevProvider, OfflinePlugin, Protected, Table, createCachePlugin, createClient, createOfflinePlugin, useAuth, useModel, useNeevClient, useSyncStatus };
289
+ export { AuthClient, AuthPlugin, CachePlugin, Form, LoggerPlugin, NeevBoundary, NeevContext, NeevProvider, OfflinePlugin, Protected, SecureStore, Table, clearPersistedStore, createCachePlugin, createClient, createOfflinePlugin, getStore, setStore, useAuth, useModel, useNeevClient, useStore, useSyncStatus };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NeevClientConfig, NeevClientInterface, AuthInterface, AuthUser, ModelRecord, UseModelReturn, SyncStatus, NeevPlugin } from '@neevjs/shared';
2
- export { ApiMeta, ApiResponse, AuthInterface, AuthLoginResponse, AuthUser, ModelRecord, NeevClientConfig, NeevClientInterface, NeevPlugin, NeevRequest, Pagination, QueuedAction, RequestOptions, SyncStatus, UseModelReturn } from '@neevjs/shared';
3
- import React from 'react';
2
+ export { ApiMeta, ApiResponse, AuthInterface, AuthLoginResponse, AuthUser, ModelRecord, NeevClientConfig, NeevClientInterface, NeevPlugin, NeevRequest, Pagination, QueuedAction, RequestOptions, SyncStatus, UseModelReturn, VERSION } from '@neevjs/shared';
3
+ import React, { ReactNode } from 'react';
4
4
 
5
5
  declare function createClient(config?: NeevClientConfig): NeevClientInterface;
6
6
 
@@ -24,25 +24,175 @@ declare class AuthClient implements AuthInterface {
24
24
  getToken(): string | null;
25
25
  }
26
26
 
27
- declare function useModel<T extends ModelRecord>(name: string): UseModelReturn<T>;
27
+ /**
28
+ * SecureStore — AES-256-GCM encrypted client-side storage for NeevJS.
29
+ *
30
+ * ## Security model
31
+ * - Data is encrypted with AES-256-GCM before being written to localStorage.
32
+ * - The encryption key is generated fresh every session and lives ONLY in memory.
33
+ * - On page refresh the key is gone, so the encrypted ciphertext in localStorage
34
+ * is permanently unreadable — it is automatically discarded.
35
+ * - This means SecureStore does NOT persist across page refreshes by design.
36
+ * If you need truly sensitive data to survive refreshes, store it on the server.
37
+ *
38
+ * ## Threat model
39
+ * ✅ Protects against: physical access to device / localStorage dump
40
+ * ✅ Protects against: another browser tab/window reading the storage
41
+ * ❌ Does NOT protect against: XSS (attacker JS runs in your origin and can
42
+ * call SecureStore.get() directly — same as any in-memory value)
43
+ *
44
+ * ## When to use
45
+ * - Temporary sensitive values during a session (payment amount, masked card, OTP state)
46
+ * - Values that must not appear in plaintext in DevTools Application → Storage
47
+ *
48
+ * ## API
49
+ * ```ts
50
+ * await SecureStore.set('pending_payment', { amount: 4999, currency: 'INR' })
51
+ * const payment = await SecureStore.get<Payment>('pending_payment')
52
+ * SecureStore.remove('pending_payment')
53
+ * SecureStore.clear() // remove all SecureStore entries
54
+ * ```
55
+ */
56
+ declare const SecureStore: {
57
+ /**
58
+ * Encrypt and store a value in localStorage.
59
+ * The value cannot be read without the in-memory session key.
60
+ */
61
+ set<T>(key: string, value: T): Promise<void>;
62
+ /**
63
+ * Retrieve and decrypt a value.
64
+ * Returns null if the key doesn't exist or cannot be decrypted
65
+ * (e.g. after a page refresh when the key is gone).
66
+ */
67
+ get<T>(key: string): Promise<T | null>;
68
+ /**
69
+ * Remove a specific key from SecureStore.
70
+ */
71
+ remove(key: string): void;
72
+ /**
73
+ * Remove ALL SecureStore entries from localStorage.
74
+ * Call this on logout to clean up all session-bound encrypted data.
75
+ */
76
+ clear(): void;
77
+ };
78
+
79
+ interface UseModelOptions {
80
+ suspense?: boolean;
81
+ /**
82
+ * Query parameters appended to the request URL.
83
+ * The full URL (including params) is used as the cache key, so
84
+ * useModel('users', { params: { page: 1 } }) and
85
+ * useModel('users', { params: { page: 2 } }) cache independently.
86
+ *
87
+ * @example params: { page: 1, role: 'admin', search: 'rahul' }
88
+ */
89
+ params?: Record<string, string | number | boolean | undefined>;
90
+ /**
91
+ * Override the client's default baseURL for this specific model instance.
92
+ * Useful for Hybrid Mode (connecting to different backends).
93
+ */
94
+ baseURL?: string;
95
+ }
96
+ declare function useModel<T extends ModelRecord>(name: string, options?: UseModelOptions): UseModelReturn<T>;
28
97
 
29
98
  declare function useAuth(): AuthInterface;
30
99
 
31
100
  declare function useSyncStatus(): SyncStatus;
32
101
 
102
+ interface UseStoreOptions {
103
+ /**
104
+ * Persist value to localStorage. Survives page reloads and browser restarts.
105
+ *
106
+ * ⚠️ SECURITY: localStorage is readable by any JavaScript on the page.
107
+ * Do NOT use for sensitive data (passwords, payment info, PII).
108
+ * Use `{ session: true }` instead for semi-sensitive data.
109
+ *
110
+ * @default false
111
+ */
112
+ persist?: boolean;
113
+ /**
114
+ * Store value in sessionStorage. Survives page refresh but is:
115
+ * - Cleared when the browser tab closes
116
+ * - NOT shared across tabs
117
+ * - Safer than localStorage for semi-sensitive data (e.g. current user preferences)
118
+ *
119
+ * Cannot be combined with `persist: true`.
120
+ *
121
+ * @default false
122
+ */
123
+ session?: boolean;
124
+ /**
125
+ * TTL (time-to-live) in milliseconds. After this duration, the stored value
126
+ * expires and falls back to `initialValue`.
127
+ * Only applies when `persist` or `session` is true.
128
+ *
129
+ * @example ttl: 7 * 24 * 60 * 60 * 1000 // 7 days
130
+ */
131
+ ttl?: number;
132
+ }
133
+ /**
134
+ * `useStore` — Global client-side state management for NeevJS.
135
+ *
136
+ * Built on React 18's `useSyncExternalStore` for concurrent-mode safety.
137
+ *
138
+ * Security levels:
139
+ * - Default (in-memory only) → Most secure. Lost on refresh.
140
+ * - `{ session: true }` → sessionStorage. Safe for semi-sensitive data.
141
+ * - `{ persist: true }` → localStorage. For non-sensitive preferences only.
142
+ *
143
+ * For truly sensitive data that must persist across sessions → store on the server.
144
+ */
145
+ declare function useStore<T>(key: string, initialValue: T, options?: UseStoreOptions): [T, (updater: T | ((prev: T) => T)) => void];
146
+ declare function getStore<T>(key: string): T | undefined;
147
+ declare function setStore<T>(key: string, value: T): void;
148
+ declare function clearPersistedStore(key: string): void;
149
+
33
150
  interface TableColumn<T extends ModelRecord> {
34
151
  key: keyof T & string;
35
152
  label?: string;
36
153
  render?: (value: T[keyof T], row: T) => React.ReactNode;
37
154
  }
155
+ interface TableClassNames {
156
+ root?: string;
157
+ table?: string;
158
+ thead?: string;
159
+ tr?: string;
160
+ th?: string;
161
+ tbody?: string;
162
+ td?: string;
163
+ actions?: string;
164
+ editButton?: string;
165
+ deleteButton?: string;
166
+ emptyState?: string;
167
+ errorState?: string;
168
+ loadingState?: string;
169
+ }
170
+ interface TableStyles {
171
+ root?: React.CSSProperties;
172
+ table?: React.CSSProperties;
173
+ thead?: React.CSSProperties;
174
+ tr?: React.CSSProperties;
175
+ th?: React.CSSProperties;
176
+ tbody?: React.CSSProperties;
177
+ td?: React.CSSProperties;
178
+ actions?: React.CSSProperties;
179
+ editButton?: React.CSSProperties;
180
+ deleteButton?: React.CSSProperties;
181
+ emptyState?: React.CSSProperties;
182
+ errorState?: React.CSSProperties;
183
+ loadingState?: React.CSSProperties;
184
+ }
38
185
  interface TableProps<T extends ModelRecord> {
39
186
  model: string;
40
187
  columns?: TableColumn<T>[];
41
188
  onEdit?: (row: T) => void;
42
189
  onDelete?: (row: T) => void;
43
190
  emptyMessage?: string;
191
+ classNames?: TableClassNames;
192
+ styles?: TableStyles;
193
+ unstyled?: boolean;
44
194
  }
45
- declare function Table<T extends ModelRecord>({ model, columns, onEdit, onDelete, emptyMessage, }: TableProps<T>): React.ReactElement;
195
+ declare function Table<T extends ModelRecord>({ model, columns, onEdit, onDelete, emptyMessage, classNames, styles, unstyled, }: TableProps<T>): React.ReactElement;
46
196
 
47
197
  interface FormField {
48
198
  name: string;
@@ -55,6 +205,26 @@ interface FormField {
55
205
  value: string;
56
206
  }[];
57
207
  }
208
+ interface FormClassNames {
209
+ root?: string;
210
+ error?: string;
211
+ fieldError?: string;
212
+ label?: string;
213
+ input?: string;
214
+ select?: string;
215
+ textarea?: string;
216
+ submitButton?: string;
217
+ }
218
+ interface FormStyles {
219
+ root?: React.CSSProperties;
220
+ error?: React.CSSProperties;
221
+ fieldError?: React.CSSProperties;
222
+ label?: React.CSSProperties;
223
+ input?: React.CSSProperties;
224
+ select?: React.CSSProperties;
225
+ textarea?: React.CSSProperties;
226
+ submitButton?: React.CSSProperties;
227
+ }
58
228
  interface FormProps<T extends ModelRecord> {
59
229
  model: string;
60
230
  fields?: FormField[];
@@ -63,16 +233,44 @@ interface FormProps<T extends ModelRecord> {
63
233
  onSuccess?: () => void;
64
234
  onError?: (err: Error) => void;
65
235
  submitLabel?: string;
236
+ classNames?: FormClassNames;
237
+ styles?: FormStyles;
238
+ unstyled?: boolean;
239
+ transformPayload?: (payload: Partial<T>) => Partial<T> | Promise<Partial<T>>;
240
+ onSubmitOverride?: (payload: Partial<T>) => Promise<void>;
66
241
  children?: React.ReactNode;
242
+ /**
243
+ * Per-field validation errors. Keys are field names, values are error messages.
244
+ * Use this to display server-side validation errors next to the relevant field.
245
+ * @example fieldErrors={{ email: 'Email is already taken' }}
246
+ */
247
+ fieldErrors?: Record<string, string>;
248
+ /**
249
+ * Client-side validation function. Called before submission.
250
+ * Return an object of field errors to block submission, or undefined/null to proceed.
251
+ * @example validate={(values) => values.password.length < 8 ? { password: 'Min 8 characters' } : undefined}
252
+ */
253
+ validate?: (values: Partial<T>) => Record<string, string> | undefined | null;
67
254
  }
68
- declare function Form<T extends ModelRecord>({ model, fields, initialValues, editId, onSuccess, onError, submitLabel, children, }: FormProps<T>): React.ReactElement;
255
+ declare function Form<T extends ModelRecord>({ model, fields, initialValues, editId, onSuccess, onError, submitLabel, classNames, styles, unstyled, transformPayload, onSubmitOverride, children, fieldErrors: externalFieldErrors, validate, }: FormProps<T>): React.ReactElement;
69
256
 
70
257
  interface ProtectedProps {
71
258
  children: React.ReactNode;
259
+ /** Shown while auth state is being verified. Defaults to null (invisible). */
260
+ loadingFallback?: React.ReactNode;
261
+ /** Shown when user is not authenticated or lacks the required role. */
72
262
  fallback?: React.ReactNode;
263
+ /** If provided, user must have this exact role string to access the content. */
73
264
  role?: string;
74
265
  }
75
- declare function Protected({ children, fallback, role }: ProtectedProps): React.ReactElement;
266
+ declare function Protected({ children, loadingFallback, fallback, role }: ProtectedProps): React.ReactElement;
267
+
268
+ interface NeevBoundaryProps {
269
+ children: ReactNode;
270
+ loadingFallback?: ReactNode;
271
+ errorFallback?: (error: Error, resetErrorBoundary: () => void) => ReactNode;
272
+ }
273
+ declare function NeevBoundary({ children, loadingFallback, errorFallback }: NeevBoundaryProps): React.ReactElement;
76
274
 
77
275
  declare const AuthPlugin: NeevPlugin;
78
276
 
@@ -88,4 +286,4 @@ declare const CachePlugin: NeevPlugin;
88
286
  declare function createOfflinePlugin(): NeevPlugin;
89
287
  declare const OfflinePlugin: NeevPlugin;
90
288
 
91
- export { AuthClient, AuthPlugin, CachePlugin, Form, LoggerPlugin, NeevContext, NeevProvider, OfflinePlugin, Protected, Table, createCachePlugin, createClient, createOfflinePlugin, useAuth, useModel, useNeevClient, useSyncStatus };
289
+ export { AuthClient, AuthPlugin, CachePlugin, Form, LoggerPlugin, NeevBoundary, NeevContext, NeevProvider, OfflinePlugin, Protected, SecureStore, Table, clearPersistedStore, createCachePlugin, createClient, createOfflinePlugin, getStore, setStore, useAuth, useModel, useNeevClient, useStore, useSyncStatus };