@pfm-platform/users-feature 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ export { useCurrentUser } from './useCurrentUser';
2
+ export type { CurrentUserProfile } from './useCurrentUser';
3
+ export { useUserSettings } from './useUserSettings';
4
+ export type { UserSettings, UserSettingsResult, } from './useUserSettings';
@@ -0,0 +1,404 @@
1
+ /**
2
+ * Hook for accessing current user profile
3
+ * Wraps useUser from data-access layer with feature-specific logic
4
+ */
5
+ import type { User } from '@pfm-platform/shared';
6
+ import type { UseQueryOptions } from '@tanstack/react-query';
7
+ export interface CurrentUserProfile {
8
+ user: User;
9
+ hasCustomSettings: boolean;
10
+ termsAccepted: boolean;
11
+ email: string;
12
+ fullName: string;
13
+ }
14
+ /**
15
+ * Get current user profile with computed properties
16
+ * Replaces usersStore.currentUser with computed flags
17
+ *
18
+ * @param options - TanStack Query options
19
+ * @returns Current user profile with computed properties
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * function UserProfile() {
24
+ * const { data: profile } = useCurrentUser();
25
+ *
26
+ * if (!profile) return null;
27
+ *
28
+ * return (
29
+ * <div>
30
+ * <h1>{profile.fullName}</h1>
31
+ * <p>{profile.email}</p>
32
+ * {!profile.termsAccepted && <TermsPrompt />}
33
+ * </div>
34
+ * );
35
+ * }
36
+ * ```
37
+ */
38
+ export declare function useCurrentUser(options?: Omit<UseQueryOptions<User>, 'queryKey' | 'queryFn'>): {
39
+ error: Error;
40
+ isError: true;
41
+ isPending: false;
42
+ isLoading: false;
43
+ isLoadingError: false;
44
+ isRefetchError: true;
45
+ isSuccess: false;
46
+ isPlaceholderData: false;
47
+ status: "error";
48
+ dataUpdatedAt: number;
49
+ errorUpdatedAt: number;
50
+ failureCount: number;
51
+ failureReason: Error | null;
52
+ errorUpdateCount: number;
53
+ isFetched: boolean;
54
+ isFetchedAfterMount: boolean;
55
+ isFetching: boolean;
56
+ isInitialLoading: boolean;
57
+ isPaused: boolean;
58
+ isRefetching: boolean;
59
+ isStale: boolean;
60
+ isEnabled: boolean;
61
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
62
+ id: string;
63
+ login: string;
64
+ email: string;
65
+ login_count: number;
66
+ last_login_at: string;
67
+ custom_tags: string[];
68
+ custom_settings: {
69
+ [x: string]: unknown;
70
+ };
71
+ first_name: string;
72
+ last_name: string;
73
+ postal_code: string;
74
+ birth_year: number;
75
+ sex: "Male" | "Female";
76
+ city?: string | undefined;
77
+ state?: string | undefined;
78
+ }, Error>>;
79
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
80
+ promise: Promise<{
81
+ id: string;
82
+ login: string;
83
+ email: string;
84
+ login_count: number;
85
+ last_login_at: string;
86
+ custom_tags: string[];
87
+ custom_settings: {
88
+ [x: string]: unknown;
89
+ };
90
+ first_name: string;
91
+ last_name: string;
92
+ postal_code: string;
93
+ birth_year: number;
94
+ sex: "Male" | "Female";
95
+ city?: string | undefined;
96
+ state?: string | undefined;
97
+ }>;
98
+ data: CurrentUserProfile | null;
99
+ } | {
100
+ error: null;
101
+ isError: false;
102
+ isPending: false;
103
+ isLoading: false;
104
+ isLoadingError: false;
105
+ isRefetchError: false;
106
+ isSuccess: true;
107
+ isPlaceholderData: false;
108
+ status: "success";
109
+ dataUpdatedAt: number;
110
+ errorUpdatedAt: number;
111
+ failureCount: number;
112
+ failureReason: Error | null;
113
+ errorUpdateCount: number;
114
+ isFetched: boolean;
115
+ isFetchedAfterMount: boolean;
116
+ isFetching: boolean;
117
+ isInitialLoading: boolean;
118
+ isPaused: boolean;
119
+ isRefetching: boolean;
120
+ isStale: boolean;
121
+ isEnabled: boolean;
122
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
123
+ id: string;
124
+ login: string;
125
+ email: string;
126
+ login_count: number;
127
+ last_login_at: string;
128
+ custom_tags: string[];
129
+ custom_settings: {
130
+ [x: string]: unknown;
131
+ };
132
+ first_name: string;
133
+ last_name: string;
134
+ postal_code: string;
135
+ birth_year: number;
136
+ sex: "Male" | "Female";
137
+ city?: string | undefined;
138
+ state?: string | undefined;
139
+ }, Error>>;
140
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
141
+ promise: Promise<{
142
+ id: string;
143
+ login: string;
144
+ email: string;
145
+ login_count: number;
146
+ last_login_at: string;
147
+ custom_tags: string[];
148
+ custom_settings: {
149
+ [x: string]: unknown;
150
+ };
151
+ first_name: string;
152
+ last_name: string;
153
+ postal_code: string;
154
+ birth_year: number;
155
+ sex: "Male" | "Female";
156
+ city?: string | undefined;
157
+ state?: string | undefined;
158
+ }>;
159
+ data: CurrentUserProfile | null;
160
+ } | {
161
+ error: Error;
162
+ isError: true;
163
+ isPending: false;
164
+ isLoading: false;
165
+ isLoadingError: true;
166
+ isRefetchError: false;
167
+ isSuccess: false;
168
+ isPlaceholderData: false;
169
+ status: "error";
170
+ dataUpdatedAt: number;
171
+ errorUpdatedAt: number;
172
+ failureCount: number;
173
+ failureReason: Error | null;
174
+ errorUpdateCount: number;
175
+ isFetched: boolean;
176
+ isFetchedAfterMount: boolean;
177
+ isFetching: boolean;
178
+ isInitialLoading: boolean;
179
+ isPaused: boolean;
180
+ isRefetching: boolean;
181
+ isStale: boolean;
182
+ isEnabled: boolean;
183
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
184
+ id: string;
185
+ login: string;
186
+ email: string;
187
+ login_count: number;
188
+ last_login_at: string;
189
+ custom_tags: string[];
190
+ custom_settings: {
191
+ [x: string]: unknown;
192
+ };
193
+ first_name: string;
194
+ last_name: string;
195
+ postal_code: string;
196
+ birth_year: number;
197
+ sex: "Male" | "Female";
198
+ city?: string | undefined;
199
+ state?: string | undefined;
200
+ }, Error>>;
201
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
202
+ promise: Promise<{
203
+ id: string;
204
+ login: string;
205
+ email: string;
206
+ login_count: number;
207
+ last_login_at: string;
208
+ custom_tags: string[];
209
+ custom_settings: {
210
+ [x: string]: unknown;
211
+ };
212
+ first_name: string;
213
+ last_name: string;
214
+ postal_code: string;
215
+ birth_year: number;
216
+ sex: "Male" | "Female";
217
+ city?: string | undefined;
218
+ state?: string | undefined;
219
+ }>;
220
+ data: CurrentUserProfile | null;
221
+ } | {
222
+ error: null;
223
+ isError: false;
224
+ isPending: true;
225
+ isLoading: true;
226
+ isLoadingError: false;
227
+ isRefetchError: false;
228
+ isSuccess: false;
229
+ isPlaceholderData: false;
230
+ status: "pending";
231
+ dataUpdatedAt: number;
232
+ errorUpdatedAt: number;
233
+ failureCount: number;
234
+ failureReason: Error | null;
235
+ errorUpdateCount: number;
236
+ isFetched: boolean;
237
+ isFetchedAfterMount: boolean;
238
+ isFetching: boolean;
239
+ isInitialLoading: boolean;
240
+ isPaused: boolean;
241
+ isRefetching: boolean;
242
+ isStale: boolean;
243
+ isEnabled: boolean;
244
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
245
+ id: string;
246
+ login: string;
247
+ email: string;
248
+ login_count: number;
249
+ last_login_at: string;
250
+ custom_tags: string[];
251
+ custom_settings: {
252
+ [x: string]: unknown;
253
+ };
254
+ first_name: string;
255
+ last_name: string;
256
+ postal_code: string;
257
+ birth_year: number;
258
+ sex: "Male" | "Female";
259
+ city?: string | undefined;
260
+ state?: string | undefined;
261
+ }, Error>>;
262
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
263
+ promise: Promise<{
264
+ id: string;
265
+ login: string;
266
+ email: string;
267
+ login_count: number;
268
+ last_login_at: string;
269
+ custom_tags: string[];
270
+ custom_settings: {
271
+ [x: string]: unknown;
272
+ };
273
+ first_name: string;
274
+ last_name: string;
275
+ postal_code: string;
276
+ birth_year: number;
277
+ sex: "Male" | "Female";
278
+ city?: string | undefined;
279
+ state?: string | undefined;
280
+ }>;
281
+ data: CurrentUserProfile | null;
282
+ } | {
283
+ error: null;
284
+ isError: false;
285
+ isPending: true;
286
+ isLoadingError: false;
287
+ isRefetchError: false;
288
+ isSuccess: false;
289
+ isPlaceholderData: false;
290
+ status: "pending";
291
+ dataUpdatedAt: number;
292
+ errorUpdatedAt: number;
293
+ failureCount: number;
294
+ failureReason: Error | null;
295
+ errorUpdateCount: number;
296
+ isFetched: boolean;
297
+ isFetchedAfterMount: boolean;
298
+ isFetching: boolean;
299
+ isLoading: boolean;
300
+ isInitialLoading: boolean;
301
+ isPaused: boolean;
302
+ isRefetching: boolean;
303
+ isStale: boolean;
304
+ isEnabled: boolean;
305
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
306
+ id: string;
307
+ login: string;
308
+ email: string;
309
+ login_count: number;
310
+ last_login_at: string;
311
+ custom_tags: string[];
312
+ custom_settings: {
313
+ [x: string]: unknown;
314
+ };
315
+ first_name: string;
316
+ last_name: string;
317
+ postal_code: string;
318
+ birth_year: number;
319
+ sex: "Male" | "Female";
320
+ city?: string | undefined;
321
+ state?: string | undefined;
322
+ }, Error>>;
323
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
324
+ promise: Promise<{
325
+ id: string;
326
+ login: string;
327
+ email: string;
328
+ login_count: number;
329
+ last_login_at: string;
330
+ custom_tags: string[];
331
+ custom_settings: {
332
+ [x: string]: unknown;
333
+ };
334
+ first_name: string;
335
+ last_name: string;
336
+ postal_code: string;
337
+ birth_year: number;
338
+ sex: "Male" | "Female";
339
+ city?: string | undefined;
340
+ state?: string | undefined;
341
+ }>;
342
+ data: CurrentUserProfile | null;
343
+ } | {
344
+ isError: false;
345
+ error: null;
346
+ isPending: false;
347
+ isLoading: false;
348
+ isLoadingError: false;
349
+ isRefetchError: false;
350
+ isSuccess: true;
351
+ isPlaceholderData: true;
352
+ status: "success";
353
+ dataUpdatedAt: number;
354
+ errorUpdatedAt: number;
355
+ failureCount: number;
356
+ failureReason: Error | null;
357
+ errorUpdateCount: number;
358
+ isFetched: boolean;
359
+ isFetchedAfterMount: boolean;
360
+ isFetching: boolean;
361
+ isInitialLoading: boolean;
362
+ isPaused: boolean;
363
+ isRefetching: boolean;
364
+ isStale: boolean;
365
+ isEnabled: boolean;
366
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
367
+ id: string;
368
+ login: string;
369
+ email: string;
370
+ login_count: number;
371
+ last_login_at: string;
372
+ custom_tags: string[];
373
+ custom_settings: {
374
+ [x: string]: unknown;
375
+ };
376
+ first_name: string;
377
+ last_name: string;
378
+ postal_code: string;
379
+ birth_year: number;
380
+ sex: "Male" | "Female";
381
+ city?: string | undefined;
382
+ state?: string | undefined;
383
+ }, Error>>;
384
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
385
+ promise: Promise<{
386
+ id: string;
387
+ login: string;
388
+ email: string;
389
+ login_count: number;
390
+ last_login_at: string;
391
+ custom_tags: string[];
392
+ custom_settings: {
393
+ [x: string]: unknown;
394
+ };
395
+ first_name: string;
396
+ last_name: string;
397
+ postal_code: string;
398
+ birth_year: number;
399
+ sex: "Male" | "Female";
400
+ city?: string | undefined;
401
+ state?: string | undefined;
402
+ }>;
403
+ data: CurrentUserProfile | null;
404
+ };
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Hook for managing user custom settings
3
+ * Provides type-safe access to nested custom_settings object
4
+ */
5
+ import type { UseQueryOptions } from '@tanstack/react-query';
6
+ import type { User } from '@pfm-platform/shared';
7
+ export interface UserSettings {
8
+ aggregation?: {
9
+ termsAccepted?: boolean;
10
+ [key: string]: any;
11
+ };
12
+ [key: string]: any;
13
+ }
14
+ export interface UserSettingsResult {
15
+ settings: UserSettings;
16
+ hasSettings: boolean;
17
+ termsAccepted: boolean;
18
+ getSetting: <T = any>(path: string) => T | undefined;
19
+ }
20
+ /**
21
+ * Get user custom settings with helper functions
22
+ * Replaces usersStore.currentUser.custom_settings access
23
+ *
24
+ * @param options - TanStack Query options
25
+ * @returns User settings with helper functions
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * function SettingsPanel() {
30
+ * const { data } = useUserSettings();
31
+ *
32
+ * if (!data) return null;
33
+ *
34
+ * return (
35
+ * <div>
36
+ * <p>Terms Accepted: {data.termsAccepted ? 'Yes' : 'No'}</p>
37
+ * <p>Custom Value: {data.getSetting('my.nested.value')}</p>
38
+ * </div>
39
+ * );
40
+ * }
41
+ * ```
42
+ */
43
+ export declare function useUserSettings(options?: Omit<UseQueryOptions<User>, 'queryKey' | 'queryFn'>): {
44
+ error: Error;
45
+ isError: true;
46
+ isPending: false;
47
+ isLoading: false;
48
+ isLoadingError: false;
49
+ isRefetchError: true;
50
+ isSuccess: false;
51
+ isPlaceholderData: false;
52
+ status: "error";
53
+ dataUpdatedAt: number;
54
+ errorUpdatedAt: number;
55
+ failureCount: number;
56
+ failureReason: Error | null;
57
+ errorUpdateCount: number;
58
+ isFetched: boolean;
59
+ isFetchedAfterMount: boolean;
60
+ isFetching: boolean;
61
+ isInitialLoading: boolean;
62
+ isPaused: boolean;
63
+ isRefetching: boolean;
64
+ isStale: boolean;
65
+ isEnabled: boolean;
66
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
67
+ id: string;
68
+ login: string;
69
+ email: string;
70
+ login_count: number;
71
+ last_login_at: string;
72
+ custom_tags: string[];
73
+ custom_settings: {
74
+ [x: string]: unknown;
75
+ };
76
+ first_name: string;
77
+ last_name: string;
78
+ postal_code: string;
79
+ birth_year: number;
80
+ sex: "Male" | "Female";
81
+ city?: string | undefined;
82
+ state?: string | undefined;
83
+ }, Error>>;
84
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
85
+ promise: Promise<{
86
+ id: string;
87
+ login: string;
88
+ email: string;
89
+ login_count: number;
90
+ last_login_at: string;
91
+ custom_tags: string[];
92
+ custom_settings: {
93
+ [x: string]: unknown;
94
+ };
95
+ first_name: string;
96
+ last_name: string;
97
+ postal_code: string;
98
+ birth_year: number;
99
+ sex: "Male" | "Female";
100
+ city?: string | undefined;
101
+ state?: string | undefined;
102
+ }>;
103
+ data: UserSettingsResult | null;
104
+ } | {
105
+ error: null;
106
+ isError: false;
107
+ isPending: false;
108
+ isLoading: false;
109
+ isLoadingError: false;
110
+ isRefetchError: false;
111
+ isSuccess: true;
112
+ isPlaceholderData: false;
113
+ status: "success";
114
+ dataUpdatedAt: number;
115
+ errorUpdatedAt: number;
116
+ failureCount: number;
117
+ failureReason: Error | null;
118
+ errorUpdateCount: number;
119
+ isFetched: boolean;
120
+ isFetchedAfterMount: boolean;
121
+ isFetching: boolean;
122
+ isInitialLoading: boolean;
123
+ isPaused: boolean;
124
+ isRefetching: boolean;
125
+ isStale: boolean;
126
+ isEnabled: boolean;
127
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
128
+ id: string;
129
+ login: string;
130
+ email: string;
131
+ login_count: number;
132
+ last_login_at: string;
133
+ custom_tags: string[];
134
+ custom_settings: {
135
+ [x: string]: unknown;
136
+ };
137
+ first_name: string;
138
+ last_name: string;
139
+ postal_code: string;
140
+ birth_year: number;
141
+ sex: "Male" | "Female";
142
+ city?: string | undefined;
143
+ state?: string | undefined;
144
+ }, Error>>;
145
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
146
+ promise: Promise<{
147
+ id: string;
148
+ login: string;
149
+ email: string;
150
+ login_count: number;
151
+ last_login_at: string;
152
+ custom_tags: string[];
153
+ custom_settings: {
154
+ [x: string]: unknown;
155
+ };
156
+ first_name: string;
157
+ last_name: string;
158
+ postal_code: string;
159
+ birth_year: number;
160
+ sex: "Male" | "Female";
161
+ city?: string | undefined;
162
+ state?: string | undefined;
163
+ }>;
164
+ data: UserSettingsResult | null;
165
+ } | {
166
+ error: Error;
167
+ isError: true;
168
+ isPending: false;
169
+ isLoading: false;
170
+ isLoadingError: true;
171
+ isRefetchError: false;
172
+ isSuccess: false;
173
+ isPlaceholderData: false;
174
+ status: "error";
175
+ dataUpdatedAt: number;
176
+ errorUpdatedAt: number;
177
+ failureCount: number;
178
+ failureReason: Error | null;
179
+ errorUpdateCount: number;
180
+ isFetched: boolean;
181
+ isFetchedAfterMount: boolean;
182
+ isFetching: boolean;
183
+ isInitialLoading: boolean;
184
+ isPaused: boolean;
185
+ isRefetching: boolean;
186
+ isStale: boolean;
187
+ isEnabled: boolean;
188
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
189
+ id: string;
190
+ login: string;
191
+ email: string;
192
+ login_count: number;
193
+ last_login_at: string;
194
+ custom_tags: string[];
195
+ custom_settings: {
196
+ [x: string]: unknown;
197
+ };
198
+ first_name: string;
199
+ last_name: string;
200
+ postal_code: string;
201
+ birth_year: number;
202
+ sex: "Male" | "Female";
203
+ city?: string | undefined;
204
+ state?: string | undefined;
205
+ }, Error>>;
206
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
207
+ promise: Promise<{
208
+ id: string;
209
+ login: string;
210
+ email: string;
211
+ login_count: number;
212
+ last_login_at: string;
213
+ custom_tags: string[];
214
+ custom_settings: {
215
+ [x: string]: unknown;
216
+ };
217
+ first_name: string;
218
+ last_name: string;
219
+ postal_code: string;
220
+ birth_year: number;
221
+ sex: "Male" | "Female";
222
+ city?: string | undefined;
223
+ state?: string | undefined;
224
+ }>;
225
+ data: UserSettingsResult | null;
226
+ } | {
227
+ error: null;
228
+ isError: false;
229
+ isPending: true;
230
+ isLoading: true;
231
+ isLoadingError: false;
232
+ isRefetchError: false;
233
+ isSuccess: false;
234
+ isPlaceholderData: false;
235
+ status: "pending";
236
+ dataUpdatedAt: number;
237
+ errorUpdatedAt: number;
238
+ failureCount: number;
239
+ failureReason: Error | null;
240
+ errorUpdateCount: number;
241
+ isFetched: boolean;
242
+ isFetchedAfterMount: boolean;
243
+ isFetching: boolean;
244
+ isInitialLoading: boolean;
245
+ isPaused: boolean;
246
+ isRefetching: boolean;
247
+ isStale: boolean;
248
+ isEnabled: boolean;
249
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
250
+ id: string;
251
+ login: string;
252
+ email: string;
253
+ login_count: number;
254
+ last_login_at: string;
255
+ custom_tags: string[];
256
+ custom_settings: {
257
+ [x: string]: unknown;
258
+ };
259
+ first_name: string;
260
+ last_name: string;
261
+ postal_code: string;
262
+ birth_year: number;
263
+ sex: "Male" | "Female";
264
+ city?: string | undefined;
265
+ state?: string | undefined;
266
+ }, Error>>;
267
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
268
+ promise: Promise<{
269
+ id: string;
270
+ login: string;
271
+ email: string;
272
+ login_count: number;
273
+ last_login_at: string;
274
+ custom_tags: string[];
275
+ custom_settings: {
276
+ [x: string]: unknown;
277
+ };
278
+ first_name: string;
279
+ last_name: string;
280
+ postal_code: string;
281
+ birth_year: number;
282
+ sex: "Male" | "Female";
283
+ city?: string | undefined;
284
+ state?: string | undefined;
285
+ }>;
286
+ data: UserSettingsResult | null;
287
+ } | {
288
+ error: null;
289
+ isError: false;
290
+ isPending: true;
291
+ isLoadingError: false;
292
+ isRefetchError: false;
293
+ isSuccess: false;
294
+ isPlaceholderData: false;
295
+ status: "pending";
296
+ dataUpdatedAt: number;
297
+ errorUpdatedAt: number;
298
+ failureCount: number;
299
+ failureReason: Error | null;
300
+ errorUpdateCount: number;
301
+ isFetched: boolean;
302
+ isFetchedAfterMount: boolean;
303
+ isFetching: boolean;
304
+ isLoading: boolean;
305
+ isInitialLoading: boolean;
306
+ isPaused: boolean;
307
+ isRefetching: boolean;
308
+ isStale: boolean;
309
+ isEnabled: boolean;
310
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
311
+ id: string;
312
+ login: string;
313
+ email: string;
314
+ login_count: number;
315
+ last_login_at: string;
316
+ custom_tags: string[];
317
+ custom_settings: {
318
+ [x: string]: unknown;
319
+ };
320
+ first_name: string;
321
+ last_name: string;
322
+ postal_code: string;
323
+ birth_year: number;
324
+ sex: "Male" | "Female";
325
+ city?: string | undefined;
326
+ state?: string | undefined;
327
+ }, Error>>;
328
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
329
+ promise: Promise<{
330
+ id: string;
331
+ login: string;
332
+ email: string;
333
+ login_count: number;
334
+ last_login_at: string;
335
+ custom_tags: string[];
336
+ custom_settings: {
337
+ [x: string]: unknown;
338
+ };
339
+ first_name: string;
340
+ last_name: string;
341
+ postal_code: string;
342
+ birth_year: number;
343
+ sex: "Male" | "Female";
344
+ city?: string | undefined;
345
+ state?: string | undefined;
346
+ }>;
347
+ data: UserSettingsResult | null;
348
+ } | {
349
+ isError: false;
350
+ error: null;
351
+ isPending: false;
352
+ isLoading: false;
353
+ isLoadingError: false;
354
+ isRefetchError: false;
355
+ isSuccess: true;
356
+ isPlaceholderData: true;
357
+ status: "success";
358
+ dataUpdatedAt: number;
359
+ errorUpdatedAt: number;
360
+ failureCount: number;
361
+ failureReason: Error | null;
362
+ errorUpdateCount: number;
363
+ isFetched: boolean;
364
+ isFetchedAfterMount: boolean;
365
+ isFetching: boolean;
366
+ isInitialLoading: boolean;
367
+ isPaused: boolean;
368
+ isRefetching: boolean;
369
+ isStale: boolean;
370
+ isEnabled: boolean;
371
+ refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
372
+ id: string;
373
+ login: string;
374
+ email: string;
375
+ login_count: number;
376
+ last_login_at: string;
377
+ custom_tags: string[];
378
+ custom_settings: {
379
+ [x: string]: unknown;
380
+ };
381
+ first_name: string;
382
+ last_name: string;
383
+ postal_code: string;
384
+ birth_year: number;
385
+ sex: "Male" | "Female";
386
+ city?: string | undefined;
387
+ state?: string | undefined;
388
+ }, Error>>;
389
+ fetchStatus: import("@tanstack/react-query").FetchStatus;
390
+ promise: Promise<{
391
+ id: string;
392
+ login: string;
393
+ email: string;
394
+ login_count: number;
395
+ last_login_at: string;
396
+ custom_tags: string[];
397
+ custom_settings: {
398
+ [x: string]: unknown;
399
+ };
400
+ first_name: string;
401
+ last_name: string;
402
+ postal_code: string;
403
+ birth_year: number;
404
+ sex: "Male" | "Female";
405
+ city?: string | undefined;
406
+ state?: string | undefined;
407
+ }>;
408
+ data: UserSettingsResult | null;
409
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var usersDataAccess = require('@pfm-platform/users-data-access');
5
+
6
+ // src/hooks/useCurrentUser.ts
7
+ function useCurrentUser(options) {
8
+ const { data: user, ...queryResult } = usersDataAccess.useCurrentUser(options);
9
+ const profile = react.useMemo(() => {
10
+ if (!user) return null;
11
+ const customSettings = user.custom_settings || {};
12
+ const hasCustomSettings = Object.keys(customSettings).length > 0;
13
+ const aggregation = customSettings.aggregation;
14
+ const termsAccepted = aggregation?.termsAccepted || false;
15
+ const fullName = `${user.first_name || ""} ${user.last_name || ""}`.trim();
16
+ return {
17
+ user,
18
+ hasCustomSettings,
19
+ termsAccepted,
20
+ email: user.email,
21
+ fullName
22
+ };
23
+ }, [user]);
24
+ return {
25
+ data: profile,
26
+ ...queryResult
27
+ };
28
+ }
29
+ function useUserSettings(options) {
30
+ const { data: user, ...queryResult } = usersDataAccess.useCurrentUser(options);
31
+ const result = react.useMemo(() => {
32
+ if (!user) return null;
33
+ const settings = user.custom_settings || {};
34
+ const hasSettings = Object.keys(settings).length > 0;
35
+ const termsAccepted = settings.aggregation?.termsAccepted || false;
36
+ const getSetting = (path) => {
37
+ return path.split(".").reduce((obj, key) => obj?.[key], settings);
38
+ };
39
+ return {
40
+ settings,
41
+ hasSettings,
42
+ termsAccepted,
43
+ getSetting
44
+ };
45
+ }, [user]);
46
+ return {
47
+ data: result,
48
+ ...queryResult
49
+ };
50
+ }
51
+
52
+ exports.useCurrentUser = useCurrentUser;
53
+ exports.useUserSettings = useUserSettings;
54
+ //# sourceMappingURL=index.cjs.map
55
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useCurrentUser.ts","../src/hooks/useUserSettings.ts"],"names":["useUserFromDataAccess","useMemo"],"mappings":";;;;;;AA0CO,SAAS,eACd,OAAA,EACA;AACA,EAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAM,GAAG,WAAA,EAAY,GAAIA,+BAAsB,OAAO,CAAA;AAEpE,EAAA,MAAM,OAAA,GAAUC,cAAQ,MAAiC;AACvD,IAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,eAAA,IAAmB,EAAC;AAChD,IAAA,MAAM,iBAAA,GAAoB,MAAA,CAAO,IAAA,CAAK,cAAc,EAAE,MAAA,GAAS,CAAA;AAG/D,IAAA,MAAM,cAAc,cAAA,CAAe,WAAA;AACnC,IAAA,MAAM,aAAA,GAAgB,aAAa,aAAA,IAAiB,KAAA;AACpD,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,IAAA,CAAK,UAAA,IAAc,EAAE,IAAI,IAAA,CAAK,SAAA,IAAa,EAAE,CAAA,CAAA,CAAG,IAAA,EAAK;AAEzE,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,iBAAA;AAAA,MACA,aAAA;AAAA,MACA,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ;AAAA,KACF;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,OAAA;AAAA,IACN,GAAG;AAAA,GACL;AACF;ACvBO,SAAS,gBACd,OAAA,EACA;AACA,EAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAM,GAAG,WAAA,EAAY,GAAID,+BAAsB,OAAO,CAAA;AAEpE,EAAA,MAAM,MAAA,GAASC,cAAQ,MAAiC;AACtD,IAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,IAAA,MAAM,QAAA,GAAyB,IAAA,CAAK,eAAA,IAAmB,EAAC;AACxD,IAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,QAAQ,EAAE,MAAA,GAAS,CAAA;AACnD,IAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,WAAA,EAAa,aAAA,IAAiB,KAAA;AAG7D,IAAA,MAAM,UAAA,GAAa,CAAU,IAAA,KAAgC;AAC3D,MAAA,OAAO,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,MAAA,CAAO,CAAC,GAAA,EAAU,GAAA,KAAQ,GAAA,GAAM,GAAG,CAAA,EAAG,QAAQ,CAAA;AAAA,IAGvE,CAAA;AAEA,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,WAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,MAAA;AAAA,IACN,GAAG;AAAA,GACL;AACF","file":"index.cjs","sourcesContent":["/**\n * Hook for accessing current user profile\n * Wraps useUser from data-access layer with feature-specific logic\n */\n\nimport { useMemo } from 'react';\nimport { useCurrentUser as useUserFromDataAccess } from '@pfm-platform/users-data-access';\nimport type { User } from '@pfm-platform/shared';\nimport type { UseQueryOptions } from '@tanstack/react-query';\n\nexport interface CurrentUserProfile {\n user: User;\n hasCustomSettings: boolean;\n termsAccepted: boolean;\n email: string;\n fullName: string;\n}\n\n/**\n * Get current user profile with computed properties\n * Replaces usersStore.currentUser with computed flags\n *\n * @param options - TanStack Query options\n * @returns Current user profile with computed properties\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { data: profile } = useCurrentUser();\n *\n * if (!profile) return null;\n *\n * return (\n * <div>\n * <h1>{profile.fullName}</h1>\n * <p>{profile.email}</p>\n * {!profile.termsAccepted && <TermsPrompt />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useCurrentUser(\n options?: Omit<UseQueryOptions<User>, 'queryKey' | 'queryFn'>\n) {\n const { data: user, ...queryResult } = useUserFromDataAccess(options);\n\n const profile = useMemo((): CurrentUserProfile | null => {\n if (!user) return null;\n\n const customSettings = user.custom_settings || {};\n const hasCustomSettings = Object.keys(customSettings).length > 0;\n\n // Type assertion for custom_settings.aggregation\n const aggregation = customSettings.aggregation as { termsAccepted?: boolean } | undefined;\n const termsAccepted = aggregation?.termsAccepted || false;\n const fullName = `${user.first_name || ''} ${user.last_name || ''}`.trim();\n\n return {\n user,\n hasCustomSettings,\n termsAccepted,\n email: user.email,\n fullName,\n };\n }, [user]);\n\n return {\n data: profile,\n ...queryResult,\n };\n}\n","/**\n * Hook for managing user custom settings\n * Provides type-safe access to nested custom_settings object\n */\n\nimport { useMemo } from 'react';\nimport { useCurrentUser as useUserFromDataAccess } from '@pfm-platform/users-data-access';\nimport type { UseQueryOptions } from '@tanstack/react-query';\nimport type { User } from '@pfm-platform/shared';\n\nexport interface UserSettings {\n aggregation?: {\n termsAccepted?: boolean;\n [key: string]: any;\n };\n [key: string]: any;\n}\n\nexport interface UserSettingsResult {\n settings: UserSettings;\n hasSettings: boolean;\n termsAccepted: boolean;\n getSetting: <T = any>(path: string) => T | undefined;\n}\n\n/**\n * Get user custom settings with helper functions\n * Replaces usersStore.currentUser.custom_settings access\n *\n * @param options - TanStack Query options\n * @returns User settings with helper functions\n *\n * @example\n * ```tsx\n * function SettingsPanel() {\n * const { data } = useUserSettings();\n *\n * if (!data) return null;\n *\n * return (\n * <div>\n * <p>Terms Accepted: {data.termsAccepted ? 'Yes' : 'No'}</p>\n * <p>Custom Value: {data.getSetting('my.nested.value')}</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useUserSettings(\n options?: Omit<UseQueryOptions<User>, 'queryKey' | 'queryFn'>\n) {\n const { data: user, ...queryResult } = useUserFromDataAccess(options);\n\n const result = useMemo((): UserSettingsResult | null => {\n if (!user) return null;\n\n const settings: UserSettings = user.custom_settings || {};\n const hasSettings = Object.keys(settings).length > 0;\n const termsAccepted = settings.aggregation?.termsAccepted || false;\n\n // Helper function to get nested setting value\n const getSetting = <T = any>(path: string): T | undefined => {\n return path.split('.').reduce((obj: any, key) => obj?.[key], settings) as\n | T\n | undefined;\n };\n\n return {\n settings,\n hasSettings,\n termsAccepted,\n getSetting,\n };\n }, [user]);\n\n return {\n data: result,\n ...queryResult,\n };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export { useCurrentUser, useUserSettings } from './hooks';
2
+ export type { CurrentUserProfile, UserSettings, UserSettingsResult, } from './hooks';
package/dist/index.js ADDED
@@ -0,0 +1,52 @@
1
+ import { useMemo } from 'react';
2
+ import { useCurrentUser as useCurrentUser$1 } from '@pfm-platform/users-data-access';
3
+
4
+ // src/hooks/useCurrentUser.ts
5
+ function useCurrentUser(options) {
6
+ const { data: user, ...queryResult } = useCurrentUser$1(options);
7
+ const profile = useMemo(() => {
8
+ if (!user) return null;
9
+ const customSettings = user.custom_settings || {};
10
+ const hasCustomSettings = Object.keys(customSettings).length > 0;
11
+ const aggregation = customSettings.aggregation;
12
+ const termsAccepted = aggregation?.termsAccepted || false;
13
+ const fullName = `${user.first_name || ""} ${user.last_name || ""}`.trim();
14
+ return {
15
+ user,
16
+ hasCustomSettings,
17
+ termsAccepted,
18
+ email: user.email,
19
+ fullName
20
+ };
21
+ }, [user]);
22
+ return {
23
+ data: profile,
24
+ ...queryResult
25
+ };
26
+ }
27
+ function useUserSettings(options) {
28
+ const { data: user, ...queryResult } = useCurrentUser$1(options);
29
+ const result = useMemo(() => {
30
+ if (!user) return null;
31
+ const settings = user.custom_settings || {};
32
+ const hasSettings = Object.keys(settings).length > 0;
33
+ const termsAccepted = settings.aggregation?.termsAccepted || false;
34
+ const getSetting = (path) => {
35
+ return path.split(".").reduce((obj, key) => obj?.[key], settings);
36
+ };
37
+ return {
38
+ settings,
39
+ hasSettings,
40
+ termsAccepted,
41
+ getSetting
42
+ };
43
+ }, [user]);
44
+ return {
45
+ data: result,
46
+ ...queryResult
47
+ };
48
+ }
49
+
50
+ export { useCurrentUser, useUserSettings };
51
+ //# sourceMappingURL=index.js.map
52
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useCurrentUser.ts","../src/hooks/useUserSettings.ts"],"names":["useUserFromDataAccess","useMemo"],"mappings":";;;;AA0CO,SAAS,eACd,OAAA,EACA;AACA,EAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAM,GAAG,WAAA,EAAY,GAAIA,iBAAsB,OAAO,CAAA;AAEpE,EAAA,MAAM,OAAA,GAAU,QAAQ,MAAiC;AACvD,IAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,eAAA,IAAmB,EAAC;AAChD,IAAA,MAAM,iBAAA,GAAoB,MAAA,CAAO,IAAA,CAAK,cAAc,EAAE,MAAA,GAAS,CAAA;AAG/D,IAAA,MAAM,cAAc,cAAA,CAAe,WAAA;AACnC,IAAA,MAAM,aAAA,GAAgB,aAAa,aAAA,IAAiB,KAAA;AACpD,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,IAAA,CAAK,UAAA,IAAc,EAAE,IAAI,IAAA,CAAK,SAAA,IAAa,EAAE,CAAA,CAAA,CAAG,IAAA,EAAK;AAEzE,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,iBAAA;AAAA,MACA,aAAA;AAAA,MACA,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ;AAAA,KACF;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,OAAA;AAAA,IACN,GAAG;AAAA,GACL;AACF;ACvBO,SAAS,gBACd,OAAA,EACA;AACA,EAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAM,GAAG,WAAA,EAAY,GAAIA,iBAAsB,OAAO,CAAA;AAEpE,EAAA,MAAM,MAAA,GAASC,QAAQ,MAAiC;AACtD,IAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,IAAA,MAAM,QAAA,GAAyB,IAAA,CAAK,eAAA,IAAmB,EAAC;AACxD,IAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,QAAQ,EAAE,MAAA,GAAS,CAAA;AACnD,IAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,WAAA,EAAa,aAAA,IAAiB,KAAA;AAG7D,IAAA,MAAM,UAAA,GAAa,CAAU,IAAA,KAAgC;AAC3D,MAAA,OAAO,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,MAAA,CAAO,CAAC,GAAA,EAAU,GAAA,KAAQ,GAAA,GAAM,GAAG,CAAA,EAAG,QAAQ,CAAA;AAAA,IAGvE,CAAA;AAEA,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,WAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,MAAA;AAAA,IACN,GAAG;AAAA,GACL;AACF","file":"index.js","sourcesContent":["/**\n * Hook for accessing current user profile\n * Wraps useUser from data-access layer with feature-specific logic\n */\n\nimport { useMemo } from 'react';\nimport { useCurrentUser as useUserFromDataAccess } from '@pfm-platform/users-data-access';\nimport type { User } from '@pfm-platform/shared';\nimport type { UseQueryOptions } from '@tanstack/react-query';\n\nexport interface CurrentUserProfile {\n user: User;\n hasCustomSettings: boolean;\n termsAccepted: boolean;\n email: string;\n fullName: string;\n}\n\n/**\n * Get current user profile with computed properties\n * Replaces usersStore.currentUser with computed flags\n *\n * @param options - TanStack Query options\n * @returns Current user profile with computed properties\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { data: profile } = useCurrentUser();\n *\n * if (!profile) return null;\n *\n * return (\n * <div>\n * <h1>{profile.fullName}</h1>\n * <p>{profile.email}</p>\n * {!profile.termsAccepted && <TermsPrompt />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useCurrentUser(\n options?: Omit<UseQueryOptions<User>, 'queryKey' | 'queryFn'>\n) {\n const { data: user, ...queryResult } = useUserFromDataAccess(options);\n\n const profile = useMemo((): CurrentUserProfile | null => {\n if (!user) return null;\n\n const customSettings = user.custom_settings || {};\n const hasCustomSettings = Object.keys(customSettings).length > 0;\n\n // Type assertion for custom_settings.aggregation\n const aggregation = customSettings.aggregation as { termsAccepted?: boolean } | undefined;\n const termsAccepted = aggregation?.termsAccepted || false;\n const fullName = `${user.first_name || ''} ${user.last_name || ''}`.trim();\n\n return {\n user,\n hasCustomSettings,\n termsAccepted,\n email: user.email,\n fullName,\n };\n }, [user]);\n\n return {\n data: profile,\n ...queryResult,\n };\n}\n","/**\n * Hook for managing user custom settings\n * Provides type-safe access to nested custom_settings object\n */\n\nimport { useMemo } from 'react';\nimport { useCurrentUser as useUserFromDataAccess } from '@pfm-platform/users-data-access';\nimport type { UseQueryOptions } from '@tanstack/react-query';\nimport type { User } from '@pfm-platform/shared';\n\nexport interface UserSettings {\n aggregation?: {\n termsAccepted?: boolean;\n [key: string]: any;\n };\n [key: string]: any;\n}\n\nexport interface UserSettingsResult {\n settings: UserSettings;\n hasSettings: boolean;\n termsAccepted: boolean;\n getSetting: <T = any>(path: string) => T | undefined;\n}\n\n/**\n * Get user custom settings with helper functions\n * Replaces usersStore.currentUser.custom_settings access\n *\n * @param options - TanStack Query options\n * @returns User settings with helper functions\n *\n * @example\n * ```tsx\n * function SettingsPanel() {\n * const { data } = useUserSettings();\n *\n * if (!data) return null;\n *\n * return (\n * <div>\n * <p>Terms Accepted: {data.termsAccepted ? 'Yes' : 'No'}</p>\n * <p>Custom Value: {data.getSetting('my.nested.value')}</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useUserSettings(\n options?: Omit<UseQueryOptions<User>, 'queryKey' | 'queryFn'>\n) {\n const { data: user, ...queryResult } = useUserFromDataAccess(options);\n\n const result = useMemo((): UserSettingsResult | null => {\n if (!user) return null;\n\n const settings: UserSettings = user.custom_settings || {};\n const hasSettings = Object.keys(settings).length > 0;\n const termsAccepted = settings.aggregation?.termsAccepted || false;\n\n // Helper function to get nested setting value\n const getSetting = <T = any>(path: string): T | undefined => {\n return path.split('.').reduce((obj: any, key) => obj?.[key], settings) as\n | T\n | undefined;\n };\n\n return {\n settings,\n hasSettings,\n termsAccepted,\n getSetting,\n };\n }, [user]);\n\n return {\n data: result,\n ...queryResult,\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@pfm-platform/users-feature",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js",
9
+ "require": "./dist/index.cjs"
10
+ }
11
+ },
12
+ "dependencies": {
13
+ "react": "19.2.0",
14
+ "@pfm-platform/shared": "0.0.1",
15
+ "@pfm-platform/users-data-access": "0.1.1"
16
+ },
17
+ "devDependencies": {
18
+ "@tanstack/react-query": "5.90.9",
19
+ "@testing-library/react": "^16.3.0",
20
+ "@types/react": "^19.2.5",
21
+ "@vitejs/plugin-react": "^5.1.1",
22
+ "@vitest/coverage-v8": "^4.0.9",
23
+ "jsdom": "^27.2.0",
24
+ "react-dom": "19.2.0",
25
+ "typescript": "5.9.3",
26
+ "vitest": "4.0.9"
27
+ },
28
+ "main": "./dist/index.js",
29
+ "module": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "files": [
32
+ "dist",
33
+ "README.md"
34
+ ],
35
+ "description": "Personal Finance Management - USERS feature layer",
36
+ "keywords": [
37
+ "pfm",
38
+ "finance",
39
+ "users",
40
+ "feature",
41
+ "react",
42
+ "typescript"
43
+ ],
44
+ "author": "Lenny Miller",
45
+ "license": "MIT",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/lennylmiller/pfm-research",
49
+ "directory": "packages/users/feature"
50
+ },
51
+ "bugs": "https://github.com/lennylmiller/pfm-research/issues",
52
+ "homepage": "https://github.com/lennylmiller/pfm-research#readme",
53
+ "scripts": {
54
+ "test": "vitest run",
55
+ "test:ui": "vitest --ui",
56
+ "build": "tsup src/index.ts --format cjs,esm --clean && pnpm run build:dts",
57
+ "build:dts": "tsc --emitDeclarationOnly --declaration --project tsconfig.tsup.json"
58
+ }
59
+ }