@pfm-platform/expenses-data-access 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.
package/dist/index.cjs ADDED
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ var reactQuery = require('@tanstack/react-query');
4
+ var shared = require('@pfm-platform/shared');
5
+ var axios = require('axios');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var axios__default = /*#__PURE__*/_interopDefault(axios);
10
+
11
+ // src/queries/useExpenses.ts
12
+ var api = axios__default.default.create({
13
+ baseURL: "/api",
14
+ headers: {
15
+ "Content-Type": "application/json"
16
+ }
17
+ });
18
+
19
+ // src/keys.ts
20
+ var expenseKeys = {
21
+ all: ["expenses"],
22
+ lists: () => [...expenseKeys.all, "list"],
23
+ list: (userId, filters) => [...expenseKeys.lists(), userId, filters]
24
+ };
25
+
26
+ // src/queries/useExpenses.ts
27
+ function useExpenses({ userId, filters }, options) {
28
+ return reactQuery.useQuery({
29
+ queryKey: expenseKeys.list(userId, filters),
30
+ queryFn: async () => {
31
+ let validatedFilters = filters;
32
+ if (filters) {
33
+ validatedFilters = shared.ExpenseSearchParamsSchema.parse(filters);
34
+ }
35
+ const params = new URLSearchParams();
36
+ if (validatedFilters?.begin_on) {
37
+ params.append("begin_on", validatedFilters.begin_on);
38
+ }
39
+ if (validatedFilters?.end_on) {
40
+ params.append("end_on", validatedFilters.end_on);
41
+ }
42
+ if (validatedFilters?.threshold !== void 0) {
43
+ params.append("threshold", validatedFilters.threshold.toString());
44
+ }
45
+ const queryString = params.toString();
46
+ const url = `/users/${userId}/expenses${queryString ? `?${queryString}` : ""}`;
47
+ const response = await api.get(url);
48
+ const validated = shared.ExpensesResponseSchema.parse(response.data);
49
+ return validated.expenses;
50
+ },
51
+ staleTime: 1e3 * 60 * 5,
52
+ // 5 minutes (aggregated data, moderate update frequency)
53
+ ...options
54
+ });
55
+ }
56
+ function useCreateExpense(options) {
57
+ const queryClient = reactQuery.useQueryClient();
58
+ const { mode } = shared.useAppMode();
59
+ return reactQuery.useMutation({
60
+ mutationFn: async ({ userId, data }) => {
61
+ const schema = mode === "admin" ? shared.ExpenseCreateSchemaAdmin : shared.ExpenseCreateSchemaUser;
62
+ const validated = schema.parse(data);
63
+ const response = await api.post(`/users/${userId}/expenses`, validated);
64
+ return shared.ExpenseSchema.parse(response.data);
65
+ },
66
+ onSuccess: (_, { userId }) => {
67
+ queryClient.invalidateQueries({
68
+ queryKey: expenseKeys.list(userId)
69
+ });
70
+ },
71
+ ...options
72
+ });
73
+ }
74
+ function useUpdateExpense(options) {
75
+ const queryClient = reactQuery.useQueryClient();
76
+ const { mode } = shared.useAppMode();
77
+ return reactQuery.useMutation({
78
+ mutationFn: async ({ userId, tag, data }) => {
79
+ const schema = mode === "admin" ? shared.ExpenseUpdateSchemaAdmin : shared.ExpenseUpdateSchemaUser;
80
+ const validated = schema.parse(data);
81
+ const response = await api.put(
82
+ `/users/${userId}/expenses/${encodeURIComponent(tag)}`,
83
+ validated
84
+ );
85
+ return shared.ExpenseSchema.parse(response.data);
86
+ },
87
+ onSuccess: (_, { userId }) => {
88
+ queryClient.invalidateQueries({
89
+ queryKey: expenseKeys.list(userId)
90
+ });
91
+ },
92
+ ...options
93
+ });
94
+ }
95
+ function useDeleteExpense(options) {
96
+ const queryClient = reactQuery.useQueryClient();
97
+ return reactQuery.useMutation({
98
+ mutationFn: async ({ userId, tag }) => {
99
+ const validated = shared.ExpenseDeleteSchema.parse({ tag });
100
+ await api.delete(`/users/${userId}/expenses/${encodeURIComponent(validated.tag)}`);
101
+ },
102
+ onSuccess: (_, { userId }) => {
103
+ queryClient.invalidateQueries({
104
+ queryKey: expenseKeys.list(userId)
105
+ });
106
+ },
107
+ ...options
108
+ });
109
+ }
110
+
111
+ exports.api = api;
112
+ exports.expenseKeys = expenseKeys;
113
+ exports.useCreateExpense = useCreateExpense;
114
+ exports.useDeleteExpense = useDeleteExpense;
115
+ exports.useExpenses = useExpenses;
116
+ exports.useUpdateExpense = useUpdateExpense;
117
+ //# sourceMappingURL=index.cjs.map
118
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/keys.ts","../src/queries/useExpenses.ts","../src/mutations/useCreateExpense.ts","../src/mutations/useUpdateExpense.ts","../src/mutations/useDeleteExpense.ts"],"names":["axios","useQuery","ExpenseSearchParamsSchema","ExpensesResponseSchema","useQueryClient","useAppMode","useMutation","ExpenseCreateSchemaAdmin","ExpenseCreateSchemaUser","ExpenseSchema","ExpenseUpdateSchemaAdmin","ExpenseUpdateSchemaUser","ExpenseDeleteSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["import axios from 'axios';\n\n/**\n * Axios instance for Expenses API\n * Base URL configured through environment variables or defaults to relative path\n */\nexport const api = axios.create({\n baseURL: '/api',\n headers: {\n 'Content-Type': 'application/json',\n },\n});\n","export const expenseKeys = {\n all: ['expenses'] as const,\n lists: () => [...expenseKeys.all, 'list'] as const,\n list: (\n userId: string,\n filters?: { begin_on?: string; end_on?: string; threshold?: number }\n ) => [...expenseKeys.lists(), userId, filters] as const,\n};\n","import { useQuery } from '@tanstack/react-query';\nimport type { UseQueryOptions } from '@tanstack/react-query';\nimport {\n ExpensesResponseSchema,\n ExpenseSearchParamsSchema,\n type Expense,\n type ExpenseSearchParams,\n} from '@pfm-platform/shared';\nimport { api } from '../client';\nimport { expenseKeys } from '../keys';\n\ninterface UseExpensesParams {\n userId: string;\n filters?: ExpenseSearchParams;\n}\n\n/**\n * Fetch expenses for a user with optional filters\n *\n * @param params - User ID and optional filters (begin_on, end_on, threshold)\n * @param options - Additional useQuery options\n * @returns Query result with expenses array\n */\nexport function useExpenses(\n { userId, filters }: UseExpensesParams,\n options?: Omit<UseQueryOptions<Expense[]>, 'queryKey' | 'queryFn'>\n) {\n return useQuery({\n queryKey: expenseKeys.list(userId, filters),\n queryFn: async () => {\n // Validate filters if provided\n let validatedFilters = filters;\n if (filters) {\n validatedFilters = ExpenseSearchParamsSchema.parse(filters);\n }\n\n const params = new URLSearchParams();\n\n if (validatedFilters?.begin_on) {\n params.append('begin_on', validatedFilters.begin_on);\n }\n if (validatedFilters?.end_on) {\n params.append('end_on', validatedFilters.end_on);\n }\n if (validatedFilters?.threshold !== undefined) {\n params.append('threshold', validatedFilters.threshold.toString());\n }\n\n const queryString = params.toString();\n const url = `/users/${userId}/expenses${queryString ? `?${queryString}` : ''}`;\n\n const response = await api.get(url);\n const validated = ExpensesResponseSchema.parse(response.data);\n return validated.expenses;\n },\n staleTime: 1000 * 60 * 5, // 5 minutes (aggregated data, moderate update frequency)\n ...options,\n });\n}\n","import {\n useMutation,\n useQueryClient,\n type UseMutationOptions,\n} from '@tanstack/react-query';\nimport {\n ExpenseCreateSchemaAdmin,\n ExpenseCreateSchemaUser,\n ExpenseSchema,\n useAppMode,\n type Expense,\n} from '@pfm-platform/shared';\nimport { api } from '../client';\nimport { expenseKeys } from '../keys';\n\n/**\n * Expense create data interface\n */\nexport interface ExpenseCreate {\n tag: string;\n amount: string;\n}\n\n/**\n * Parameters for useCreateExpense mutation\n */\nexport interface CreateExpenseParams {\n userId: string;\n data: ExpenseCreate;\n}\n\n/**\n * Mutation hook for creating a new expense\n *\n * Creates an expense record manually for testing purposes.\n * Uses mode switching for validation:\n * - Admin mode: Allows manual expense creation for test data\n * - User mode: Blocked (expenses are computed from transactions)\n *\n * @example\n * ```tsx\n * const createExpense = useCreateExpense();\n *\n * createExpense.mutate({\n * userId: 'user123',\n * data: {\n * tag: 'groceries',\n * amount: '123.45'\n * }\n * });\n * ```\n */\nexport function useCreateExpense(\n options?: Omit<UseMutationOptions<Expense, Error, CreateExpenseParams>, 'mutationFn'>\n) {\n const queryClient = useQueryClient();\n const { mode } = useAppMode();\n\n return useMutation({\n mutationFn: async ({ userId, data }: CreateExpenseParams) => {\n // Select schema based on mode\n const schema =\n mode === 'admin'\n ? ExpenseCreateSchemaAdmin\n : ExpenseCreateSchemaUser;\n\n // Validate expense data\n const validated = schema.parse(data);\n\n // POST to create new expense\n const response = await api.post(`/users/${userId}/expenses`, validated);\n\n // Return created expense\n return ExpenseSchema.parse(response.data);\n },\n onSuccess: (_, { userId }) => {\n // Invalidate expenses query to refetch updated list\n queryClient.invalidateQueries({\n queryKey: expenseKeys.list(userId),\n });\n },\n ...options,\n });\n}\n","import {\n useMutation,\n useQueryClient,\n type UseMutationOptions,\n} from '@tanstack/react-query';\nimport {\n ExpenseUpdateSchemaAdmin,\n ExpenseUpdateSchemaUser,\n ExpenseSchema,\n useAppMode,\n type Expense,\n} from '@pfm-platform/shared';\nimport { api } from '../client';\nimport { expenseKeys } from '../keys';\n\n/**\n * Expense update data interface\n */\nexport interface ExpenseUpdate {\n amount?: string;\n}\n\n/**\n * Parameters for useUpdateExpense mutation\n */\nexport interface UpdateExpenseParams {\n userId: string;\n tag: string;\n data: ExpenseUpdate;\n}\n\n/**\n * Mutation hook for updating an expense\n *\n * Updates expense fields for testing purposes.\n * Uses mode switching for validation:\n * - Admin mode: Allows updating expenses for test scenarios\n * - User mode: Blocked (expenses are computed from transactions)\n *\n * @example\n * ```tsx\n * const updateExpense = useUpdateExpense();\n *\n * updateExpense.mutate({\n * userId: 'user123',\n * tag: 'groceries',\n * data: {\n * amount: '200.00'\n * }\n * });\n * ```\n */\nexport function useUpdateExpense(\n options?: Omit<UseMutationOptions<Expense, Error, UpdateExpenseParams>, 'mutationFn'>\n) {\n const queryClient = useQueryClient();\n const { mode } = useAppMode();\n\n return useMutation({\n mutationFn: async ({ userId, tag, data }: UpdateExpenseParams) => {\n // Select schema based on mode\n const schema =\n mode === 'admin'\n ? ExpenseUpdateSchemaAdmin\n : ExpenseUpdateSchemaUser;\n\n // Validate expense update data\n const validated = schema.parse(data);\n\n // PUT to update expense\n const response = await api.put(\n `/users/${userId}/expenses/${encodeURIComponent(tag)}`,\n validated\n );\n\n // Return updated expense\n return ExpenseSchema.parse(response.data);\n },\n onSuccess: (_, { userId }) => {\n // Invalidate expenses query to refetch updated list\n queryClient.invalidateQueries({\n queryKey: expenseKeys.list(userId),\n });\n },\n ...options,\n });\n}\n","import {\n useMutation,\n useQueryClient,\n type UseMutationOptions,\n} from '@tanstack/react-query';\nimport { ExpenseDeleteSchema } from '@pfm-platform/shared';\nimport { api } from '../client';\nimport { expenseKeys } from '../keys';\n\n/**\n * Parameters for useDeleteExpense mutation\n */\nexport interface DeleteExpenseParams {\n userId: string;\n tag: string;\n}\n\n/**\n * Mutation hook for deleting an expense\n *\n * Deletes an expense record from the system.\n * Available in both admin and user modes (for test data cleanup).\n *\n * @example\n * ```tsx\n * const deleteExpense = useDeleteExpense();\n *\n * deleteExpense.mutate({\n * userId: 'user123',\n * tag: 'groceries'\n * });\n * ```\n */\nexport function useDeleteExpense(\n options?: Omit<UseMutationOptions<void, Error, DeleteExpenseParams>, 'mutationFn'>\n) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: async ({ userId, tag }: DeleteExpenseParams) => {\n // Validate expense tag\n const validated = ExpenseDeleteSchema.parse({ tag });\n\n // DELETE expense\n await api.delete(`/users/${userId}/expenses/${encodeURIComponent(validated.tag)}`);\n },\n onSuccess: (_, { userId }) => {\n // Invalidate expenses query to refetch updated list\n queryClient.invalidateQueries({\n queryKey: expenseKeys.list(userId),\n });\n },\n ...options,\n });\n}\n"]}
@@ -0,0 +1,147 @@
1
+ import * as _tanstack_react_query from '@tanstack/react-query';
2
+ import { UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
3
+ import { ExpenseSearchParams, Expense } from '@pfm-platform/shared';
4
+ import * as axios from 'axios';
5
+
6
+ interface UseExpensesParams {
7
+ userId: string;
8
+ filters?: ExpenseSearchParams;
9
+ }
10
+ /**
11
+ * Fetch expenses for a user with optional filters
12
+ *
13
+ * @param params - User ID and optional filters (begin_on, end_on, threshold)
14
+ * @param options - Additional useQuery options
15
+ * @returns Query result with expenses array
16
+ */
17
+ declare function useExpenses({ userId, filters }: UseExpensesParams, options?: Omit<UseQueryOptions<Expense[]>, 'queryKey' | 'queryFn'>): _tanstack_react_query.UseQueryResult<{
18
+ tag: string;
19
+ amount: string;
20
+ }[], Error>;
21
+
22
+ /**
23
+ * Expense create data interface
24
+ */
25
+ interface ExpenseCreate {
26
+ tag: string;
27
+ amount: string;
28
+ }
29
+ /**
30
+ * Parameters for useCreateExpense mutation
31
+ */
32
+ interface CreateExpenseParams {
33
+ userId: string;
34
+ data: ExpenseCreate;
35
+ }
36
+ /**
37
+ * Mutation hook for creating a new expense
38
+ *
39
+ * Creates an expense record manually for testing purposes.
40
+ * Uses mode switching for validation:
41
+ * - Admin mode: Allows manual expense creation for test data
42
+ * - User mode: Blocked (expenses are computed from transactions)
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * const createExpense = useCreateExpense();
47
+ *
48
+ * createExpense.mutate({
49
+ * userId: 'user123',
50
+ * data: {
51
+ * tag: 'groceries',
52
+ * amount: '123.45'
53
+ * }
54
+ * });
55
+ * ```
56
+ */
57
+ declare function useCreateExpense(options?: Omit<UseMutationOptions<Expense, Error, CreateExpenseParams>, 'mutationFn'>): _tanstack_react_query.UseMutationResult<{
58
+ tag: string;
59
+ amount: string;
60
+ }, Error, CreateExpenseParams, unknown>;
61
+
62
+ /**
63
+ * Expense update data interface
64
+ */
65
+ interface ExpenseUpdate {
66
+ amount?: string;
67
+ }
68
+ /**
69
+ * Parameters for useUpdateExpense mutation
70
+ */
71
+ interface UpdateExpenseParams {
72
+ userId: string;
73
+ tag: string;
74
+ data: ExpenseUpdate;
75
+ }
76
+ /**
77
+ * Mutation hook for updating an expense
78
+ *
79
+ * Updates expense fields for testing purposes.
80
+ * Uses mode switching for validation:
81
+ * - Admin mode: Allows updating expenses for test scenarios
82
+ * - User mode: Blocked (expenses are computed from transactions)
83
+ *
84
+ * @example
85
+ * ```tsx
86
+ * const updateExpense = useUpdateExpense();
87
+ *
88
+ * updateExpense.mutate({
89
+ * userId: 'user123',
90
+ * tag: 'groceries',
91
+ * data: {
92
+ * amount: '200.00'
93
+ * }
94
+ * });
95
+ * ```
96
+ */
97
+ declare function useUpdateExpense(options?: Omit<UseMutationOptions<Expense, Error, UpdateExpenseParams>, 'mutationFn'>): _tanstack_react_query.UseMutationResult<{
98
+ tag: string;
99
+ amount: string;
100
+ }, Error, UpdateExpenseParams, unknown>;
101
+
102
+ /**
103
+ * Parameters for useDeleteExpense mutation
104
+ */
105
+ interface DeleteExpenseParams {
106
+ userId: string;
107
+ tag: string;
108
+ }
109
+ /**
110
+ * Mutation hook for deleting an expense
111
+ *
112
+ * Deletes an expense record from the system.
113
+ * Available in both admin and user modes (for test data cleanup).
114
+ *
115
+ * @example
116
+ * ```tsx
117
+ * const deleteExpense = useDeleteExpense();
118
+ *
119
+ * deleteExpense.mutate({
120
+ * userId: 'user123',
121
+ * tag: 'groceries'
122
+ * });
123
+ * ```
124
+ */
125
+ declare function useDeleteExpense(options?: Omit<UseMutationOptions<void, Error, DeleteExpenseParams>, 'mutationFn'>): _tanstack_react_query.UseMutationResult<void, Error, DeleteExpenseParams, unknown>;
126
+
127
+ declare const expenseKeys: {
128
+ all: readonly ["expenses"];
129
+ lists: () => readonly ["expenses", "list"];
130
+ list: (userId: string, filters?: {
131
+ begin_on?: string;
132
+ end_on?: string;
133
+ threshold?: number;
134
+ }) => readonly ["expenses", "list", string, {
135
+ begin_on?: string;
136
+ end_on?: string;
137
+ threshold?: number;
138
+ } | undefined];
139
+ };
140
+
141
+ /**
142
+ * Axios instance for Expenses API
143
+ * Base URL configured through environment variables or defaults to relative path
144
+ */
145
+ declare const api: axios.AxiosInstance;
146
+
147
+ export { type CreateExpenseParams, type DeleteExpenseParams, type ExpenseCreate, type ExpenseUpdate, type UpdateExpenseParams, api, expenseKeys, useCreateExpense, useDeleteExpense, useExpenses, useUpdateExpense };
@@ -0,0 +1,147 @@
1
+ import * as _tanstack_react_query from '@tanstack/react-query';
2
+ import { UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
3
+ import { ExpenseSearchParams, Expense } from '@pfm-platform/shared';
4
+ import * as axios from 'axios';
5
+
6
+ interface UseExpensesParams {
7
+ userId: string;
8
+ filters?: ExpenseSearchParams;
9
+ }
10
+ /**
11
+ * Fetch expenses for a user with optional filters
12
+ *
13
+ * @param params - User ID and optional filters (begin_on, end_on, threshold)
14
+ * @param options - Additional useQuery options
15
+ * @returns Query result with expenses array
16
+ */
17
+ declare function useExpenses({ userId, filters }: UseExpensesParams, options?: Omit<UseQueryOptions<Expense[]>, 'queryKey' | 'queryFn'>): _tanstack_react_query.UseQueryResult<{
18
+ tag: string;
19
+ amount: string;
20
+ }[], Error>;
21
+
22
+ /**
23
+ * Expense create data interface
24
+ */
25
+ interface ExpenseCreate {
26
+ tag: string;
27
+ amount: string;
28
+ }
29
+ /**
30
+ * Parameters for useCreateExpense mutation
31
+ */
32
+ interface CreateExpenseParams {
33
+ userId: string;
34
+ data: ExpenseCreate;
35
+ }
36
+ /**
37
+ * Mutation hook for creating a new expense
38
+ *
39
+ * Creates an expense record manually for testing purposes.
40
+ * Uses mode switching for validation:
41
+ * - Admin mode: Allows manual expense creation for test data
42
+ * - User mode: Blocked (expenses are computed from transactions)
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * const createExpense = useCreateExpense();
47
+ *
48
+ * createExpense.mutate({
49
+ * userId: 'user123',
50
+ * data: {
51
+ * tag: 'groceries',
52
+ * amount: '123.45'
53
+ * }
54
+ * });
55
+ * ```
56
+ */
57
+ declare function useCreateExpense(options?: Omit<UseMutationOptions<Expense, Error, CreateExpenseParams>, 'mutationFn'>): _tanstack_react_query.UseMutationResult<{
58
+ tag: string;
59
+ amount: string;
60
+ }, Error, CreateExpenseParams, unknown>;
61
+
62
+ /**
63
+ * Expense update data interface
64
+ */
65
+ interface ExpenseUpdate {
66
+ amount?: string;
67
+ }
68
+ /**
69
+ * Parameters for useUpdateExpense mutation
70
+ */
71
+ interface UpdateExpenseParams {
72
+ userId: string;
73
+ tag: string;
74
+ data: ExpenseUpdate;
75
+ }
76
+ /**
77
+ * Mutation hook for updating an expense
78
+ *
79
+ * Updates expense fields for testing purposes.
80
+ * Uses mode switching for validation:
81
+ * - Admin mode: Allows updating expenses for test scenarios
82
+ * - User mode: Blocked (expenses are computed from transactions)
83
+ *
84
+ * @example
85
+ * ```tsx
86
+ * const updateExpense = useUpdateExpense();
87
+ *
88
+ * updateExpense.mutate({
89
+ * userId: 'user123',
90
+ * tag: 'groceries',
91
+ * data: {
92
+ * amount: '200.00'
93
+ * }
94
+ * });
95
+ * ```
96
+ */
97
+ declare function useUpdateExpense(options?: Omit<UseMutationOptions<Expense, Error, UpdateExpenseParams>, 'mutationFn'>): _tanstack_react_query.UseMutationResult<{
98
+ tag: string;
99
+ amount: string;
100
+ }, Error, UpdateExpenseParams, unknown>;
101
+
102
+ /**
103
+ * Parameters for useDeleteExpense mutation
104
+ */
105
+ interface DeleteExpenseParams {
106
+ userId: string;
107
+ tag: string;
108
+ }
109
+ /**
110
+ * Mutation hook for deleting an expense
111
+ *
112
+ * Deletes an expense record from the system.
113
+ * Available in both admin and user modes (for test data cleanup).
114
+ *
115
+ * @example
116
+ * ```tsx
117
+ * const deleteExpense = useDeleteExpense();
118
+ *
119
+ * deleteExpense.mutate({
120
+ * userId: 'user123',
121
+ * tag: 'groceries'
122
+ * });
123
+ * ```
124
+ */
125
+ declare function useDeleteExpense(options?: Omit<UseMutationOptions<void, Error, DeleteExpenseParams>, 'mutationFn'>): _tanstack_react_query.UseMutationResult<void, Error, DeleteExpenseParams, unknown>;
126
+
127
+ declare const expenseKeys: {
128
+ all: readonly ["expenses"];
129
+ lists: () => readonly ["expenses", "list"];
130
+ list: (userId: string, filters?: {
131
+ begin_on?: string;
132
+ end_on?: string;
133
+ threshold?: number;
134
+ }) => readonly ["expenses", "list", string, {
135
+ begin_on?: string;
136
+ end_on?: string;
137
+ threshold?: number;
138
+ } | undefined];
139
+ };
140
+
141
+ /**
142
+ * Axios instance for Expenses API
143
+ * Base URL configured through environment variables or defaults to relative path
144
+ */
145
+ declare const api: axios.AxiosInstance;
146
+
147
+ export { type CreateExpenseParams, type DeleteExpenseParams, type ExpenseCreate, type ExpenseUpdate, type UpdateExpenseParams, api, expenseKeys, useCreateExpense, useDeleteExpense, useExpenses, useUpdateExpense };
package/dist/index.js ADDED
@@ -0,0 +1,107 @@
1
+ import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
2
+ import { ExpenseSearchParamsSchema, ExpensesResponseSchema, useAppMode, ExpenseCreateSchemaAdmin, ExpenseCreateSchemaUser, ExpenseSchema, ExpenseUpdateSchemaAdmin, ExpenseUpdateSchemaUser, ExpenseDeleteSchema } from '@pfm-platform/shared';
3
+ import axios from 'axios';
4
+
5
+ // src/queries/useExpenses.ts
6
+ var api = axios.create({
7
+ baseURL: "/api",
8
+ headers: {
9
+ "Content-Type": "application/json"
10
+ }
11
+ });
12
+
13
+ // src/keys.ts
14
+ var expenseKeys = {
15
+ all: ["expenses"],
16
+ lists: () => [...expenseKeys.all, "list"],
17
+ list: (userId, filters) => [...expenseKeys.lists(), userId, filters]
18
+ };
19
+
20
+ // src/queries/useExpenses.ts
21
+ function useExpenses({ userId, filters }, options) {
22
+ return useQuery({
23
+ queryKey: expenseKeys.list(userId, filters),
24
+ queryFn: async () => {
25
+ let validatedFilters = filters;
26
+ if (filters) {
27
+ validatedFilters = ExpenseSearchParamsSchema.parse(filters);
28
+ }
29
+ const params = new URLSearchParams();
30
+ if (validatedFilters?.begin_on) {
31
+ params.append("begin_on", validatedFilters.begin_on);
32
+ }
33
+ if (validatedFilters?.end_on) {
34
+ params.append("end_on", validatedFilters.end_on);
35
+ }
36
+ if (validatedFilters?.threshold !== void 0) {
37
+ params.append("threshold", validatedFilters.threshold.toString());
38
+ }
39
+ const queryString = params.toString();
40
+ const url = `/users/${userId}/expenses${queryString ? `?${queryString}` : ""}`;
41
+ const response = await api.get(url);
42
+ const validated = ExpensesResponseSchema.parse(response.data);
43
+ return validated.expenses;
44
+ },
45
+ staleTime: 1e3 * 60 * 5,
46
+ // 5 minutes (aggregated data, moderate update frequency)
47
+ ...options
48
+ });
49
+ }
50
+ function useCreateExpense(options) {
51
+ const queryClient = useQueryClient();
52
+ const { mode } = useAppMode();
53
+ return useMutation({
54
+ mutationFn: async ({ userId, data }) => {
55
+ const schema = mode === "admin" ? ExpenseCreateSchemaAdmin : ExpenseCreateSchemaUser;
56
+ const validated = schema.parse(data);
57
+ const response = await api.post(`/users/${userId}/expenses`, validated);
58
+ return ExpenseSchema.parse(response.data);
59
+ },
60
+ onSuccess: (_, { userId }) => {
61
+ queryClient.invalidateQueries({
62
+ queryKey: expenseKeys.list(userId)
63
+ });
64
+ },
65
+ ...options
66
+ });
67
+ }
68
+ function useUpdateExpense(options) {
69
+ const queryClient = useQueryClient();
70
+ const { mode } = useAppMode();
71
+ return useMutation({
72
+ mutationFn: async ({ userId, tag, data }) => {
73
+ const schema = mode === "admin" ? ExpenseUpdateSchemaAdmin : ExpenseUpdateSchemaUser;
74
+ const validated = schema.parse(data);
75
+ const response = await api.put(
76
+ `/users/${userId}/expenses/${encodeURIComponent(tag)}`,
77
+ validated
78
+ );
79
+ return ExpenseSchema.parse(response.data);
80
+ },
81
+ onSuccess: (_, { userId }) => {
82
+ queryClient.invalidateQueries({
83
+ queryKey: expenseKeys.list(userId)
84
+ });
85
+ },
86
+ ...options
87
+ });
88
+ }
89
+ function useDeleteExpense(options) {
90
+ const queryClient = useQueryClient();
91
+ return useMutation({
92
+ mutationFn: async ({ userId, tag }) => {
93
+ const validated = ExpenseDeleteSchema.parse({ tag });
94
+ await api.delete(`/users/${userId}/expenses/${encodeURIComponent(validated.tag)}`);
95
+ },
96
+ onSuccess: (_, { userId }) => {
97
+ queryClient.invalidateQueries({
98
+ queryKey: expenseKeys.list(userId)
99
+ });
100
+ },
101
+ ...options
102
+ });
103
+ }
104
+
105
+ export { api, expenseKeys, useCreateExpense, useDeleteExpense, useExpenses, useUpdateExpense };
106
+ //# sourceMappingURL=index.js.map
107
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/keys.ts","../src/queries/useExpenses.ts","../src/mutations/useCreateExpense.ts","../src/mutations/useUpdateExpense.ts","../src/mutations/useDeleteExpense.ts"],"names":["useQueryClient","useAppMode","useMutation","ExpenseSchema"],"mappings":"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","file":"index.js","sourcesContent":["import axios from 'axios';\n\n/**\n * Axios instance for Expenses API\n * Base URL configured through environment variables or defaults to relative path\n */\nexport const api = axios.create({\n baseURL: '/api',\n headers: {\n 'Content-Type': 'application/json',\n },\n});\n","export const expenseKeys = {\n all: ['expenses'] as const,\n lists: () => [...expenseKeys.all, 'list'] as const,\n list: (\n userId: string,\n filters?: { begin_on?: string; end_on?: string; threshold?: number }\n ) => [...expenseKeys.lists(), userId, filters] as const,\n};\n","import { useQuery } from '@tanstack/react-query';\nimport type { UseQueryOptions } from '@tanstack/react-query';\nimport {\n ExpensesResponseSchema,\n ExpenseSearchParamsSchema,\n type Expense,\n type ExpenseSearchParams,\n} from '@pfm-platform/shared';\nimport { api } from '../client';\nimport { expenseKeys } from '../keys';\n\ninterface UseExpensesParams {\n userId: string;\n filters?: ExpenseSearchParams;\n}\n\n/**\n * Fetch expenses for a user with optional filters\n *\n * @param params - User ID and optional filters (begin_on, end_on, threshold)\n * @param options - Additional useQuery options\n * @returns Query result with expenses array\n */\nexport function useExpenses(\n { userId, filters }: UseExpensesParams,\n options?: Omit<UseQueryOptions<Expense[]>, 'queryKey' | 'queryFn'>\n) {\n return useQuery({\n queryKey: expenseKeys.list(userId, filters),\n queryFn: async () => {\n // Validate filters if provided\n let validatedFilters = filters;\n if (filters) {\n validatedFilters = ExpenseSearchParamsSchema.parse(filters);\n }\n\n const params = new URLSearchParams();\n\n if (validatedFilters?.begin_on) {\n params.append('begin_on', validatedFilters.begin_on);\n }\n if (validatedFilters?.end_on) {\n params.append('end_on', validatedFilters.end_on);\n }\n if (validatedFilters?.threshold !== undefined) {\n params.append('threshold', validatedFilters.threshold.toString());\n }\n\n const queryString = params.toString();\n const url = `/users/${userId}/expenses${queryString ? `?${queryString}` : ''}`;\n\n const response = await api.get(url);\n const validated = ExpensesResponseSchema.parse(response.data);\n return validated.expenses;\n },\n staleTime: 1000 * 60 * 5, // 5 minutes (aggregated data, moderate update frequency)\n ...options,\n });\n}\n","import {\n useMutation,\n useQueryClient,\n type UseMutationOptions,\n} from '@tanstack/react-query';\nimport {\n ExpenseCreateSchemaAdmin,\n ExpenseCreateSchemaUser,\n ExpenseSchema,\n useAppMode,\n type Expense,\n} from '@pfm-platform/shared';\nimport { api } from '../client';\nimport { expenseKeys } from '../keys';\n\n/**\n * Expense create data interface\n */\nexport interface ExpenseCreate {\n tag: string;\n amount: string;\n}\n\n/**\n * Parameters for useCreateExpense mutation\n */\nexport interface CreateExpenseParams {\n userId: string;\n data: ExpenseCreate;\n}\n\n/**\n * Mutation hook for creating a new expense\n *\n * Creates an expense record manually for testing purposes.\n * Uses mode switching for validation:\n * - Admin mode: Allows manual expense creation for test data\n * - User mode: Blocked (expenses are computed from transactions)\n *\n * @example\n * ```tsx\n * const createExpense = useCreateExpense();\n *\n * createExpense.mutate({\n * userId: 'user123',\n * data: {\n * tag: 'groceries',\n * amount: '123.45'\n * }\n * });\n * ```\n */\nexport function useCreateExpense(\n options?: Omit<UseMutationOptions<Expense, Error, CreateExpenseParams>, 'mutationFn'>\n) {\n const queryClient = useQueryClient();\n const { mode } = useAppMode();\n\n return useMutation({\n mutationFn: async ({ userId, data }: CreateExpenseParams) => {\n // Select schema based on mode\n const schema =\n mode === 'admin'\n ? ExpenseCreateSchemaAdmin\n : ExpenseCreateSchemaUser;\n\n // Validate expense data\n const validated = schema.parse(data);\n\n // POST to create new expense\n const response = await api.post(`/users/${userId}/expenses`, validated);\n\n // Return created expense\n return ExpenseSchema.parse(response.data);\n },\n onSuccess: (_, { userId }) => {\n // Invalidate expenses query to refetch updated list\n queryClient.invalidateQueries({\n queryKey: expenseKeys.list(userId),\n });\n },\n ...options,\n });\n}\n","import {\n useMutation,\n useQueryClient,\n type UseMutationOptions,\n} from '@tanstack/react-query';\nimport {\n ExpenseUpdateSchemaAdmin,\n ExpenseUpdateSchemaUser,\n ExpenseSchema,\n useAppMode,\n type Expense,\n} from '@pfm-platform/shared';\nimport { api } from '../client';\nimport { expenseKeys } from '../keys';\n\n/**\n * Expense update data interface\n */\nexport interface ExpenseUpdate {\n amount?: string;\n}\n\n/**\n * Parameters for useUpdateExpense mutation\n */\nexport interface UpdateExpenseParams {\n userId: string;\n tag: string;\n data: ExpenseUpdate;\n}\n\n/**\n * Mutation hook for updating an expense\n *\n * Updates expense fields for testing purposes.\n * Uses mode switching for validation:\n * - Admin mode: Allows updating expenses for test scenarios\n * - User mode: Blocked (expenses are computed from transactions)\n *\n * @example\n * ```tsx\n * const updateExpense = useUpdateExpense();\n *\n * updateExpense.mutate({\n * userId: 'user123',\n * tag: 'groceries',\n * data: {\n * amount: '200.00'\n * }\n * });\n * ```\n */\nexport function useUpdateExpense(\n options?: Omit<UseMutationOptions<Expense, Error, UpdateExpenseParams>, 'mutationFn'>\n) {\n const queryClient = useQueryClient();\n const { mode } = useAppMode();\n\n return useMutation({\n mutationFn: async ({ userId, tag, data }: UpdateExpenseParams) => {\n // Select schema based on mode\n const schema =\n mode === 'admin'\n ? ExpenseUpdateSchemaAdmin\n : ExpenseUpdateSchemaUser;\n\n // Validate expense update data\n const validated = schema.parse(data);\n\n // PUT to update expense\n const response = await api.put(\n `/users/${userId}/expenses/${encodeURIComponent(tag)}`,\n validated\n );\n\n // Return updated expense\n return ExpenseSchema.parse(response.data);\n },\n onSuccess: (_, { userId }) => {\n // Invalidate expenses query to refetch updated list\n queryClient.invalidateQueries({\n queryKey: expenseKeys.list(userId),\n });\n },\n ...options,\n });\n}\n","import {\n useMutation,\n useQueryClient,\n type UseMutationOptions,\n} from '@tanstack/react-query';\nimport { ExpenseDeleteSchema } from '@pfm-platform/shared';\nimport { api } from '../client';\nimport { expenseKeys } from '../keys';\n\n/**\n * Parameters for useDeleteExpense mutation\n */\nexport interface DeleteExpenseParams {\n userId: string;\n tag: string;\n}\n\n/**\n * Mutation hook for deleting an expense\n *\n * Deletes an expense record from the system.\n * Available in both admin and user modes (for test data cleanup).\n *\n * @example\n * ```tsx\n * const deleteExpense = useDeleteExpense();\n *\n * deleteExpense.mutate({\n * userId: 'user123',\n * tag: 'groceries'\n * });\n * ```\n */\nexport function useDeleteExpense(\n options?: Omit<UseMutationOptions<void, Error, DeleteExpenseParams>, 'mutationFn'>\n) {\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: async ({ userId, tag }: DeleteExpenseParams) => {\n // Validate expense tag\n const validated = ExpenseDeleteSchema.parse({ tag });\n\n // DELETE expense\n await api.delete(`/users/${userId}/expenses/${encodeURIComponent(validated.tag)}`);\n },\n onSuccess: (_, { userId }) => {\n // Invalidate expenses query to refetch updated list\n queryClient.invalidateQueries({\n queryKey: expenseKeys.list(userId),\n });\n },\n ...options,\n });\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@pfm-platform/expenses-data-access",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "dependencies": {
7
+ "axios": "^1.13.2",
8
+ "zod": "4.1.12",
9
+ "@pfm-platform/shared": "0.0.1"
10
+ },
11
+ "devDependencies": {
12
+ "@testing-library/react": "^16.3.0",
13
+ "@vitejs/plugin-react": "^5.1.1",
14
+ "@vitest/coverage-v8": "^4.0.9",
15
+ "jsdom": "^27.2.0",
16
+ "react-dom": "19.2.0",
17
+ "typescript": "5.9.3",
18
+ "vitest": "4.0.9"
19
+ },
20
+ "module": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js",
26
+ "require": "./dist/index.cjs"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md"
32
+ ],
33
+ "description": "Personal Finance Management - EXPENSES data-access layer",
34
+ "keywords": [
35
+ "pfm",
36
+ "finance",
37
+ "expenses",
38
+ "data-access",
39
+ "react",
40
+ "typescript"
41
+ ],
42
+ "author": "Lenny Miller",
43
+ "license": "MIT",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/lennylmiller/pfm-research",
47
+ "directory": "packages/expenses/data-access"
48
+ },
49
+ "bugs": "https://github.com/lennylmiller/pfm-research/issues",
50
+ "homepage": "https://github.com/lennylmiller/pfm-research#readme",
51
+ "peerDependencies": {
52
+ "@tanstack/react-query": "5.90.9",
53
+ "react": "19.2.0"
54
+ },
55
+ "scripts": {
56
+ "test": "vitest run",
57
+ "test:watch": "vitest",
58
+ "test:coverage": "vitest run --coverage",
59
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean"
60
+ }
61
+ }