@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 +21 -0
- package/dist/index.d.mts +205 -7
- package/dist/index.d.ts +205 -7
- package/dist/index.js +662 -139
- package/dist/index.mjs +652 -134
- package/package.json +10 -6
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
|
-
|
|
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
|
-
|
|
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 };
|