@enfyra/sdk-nuxt 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -143,6 +143,9 @@ await execute({
143
143
  - `query?: Record<string, any>` - URL query parameters
144
144
  - `headers?: Record<string, string>` - Custom headers
145
145
  - `errorContext?: string` - Error context for logging
146
+ - `onError?: (error: ApiError, context?: string) => void` - Custom error handler
147
+ - `batchSize?: number` - Batch size for chunking large operations (default: no limit)
148
+ - `concurrent?: number` - Maximum concurrent requests (default: no limit)
146
149
  - `key?: string` - Cache key (SSR mode, optional)
147
150
  - `default?: () => T` - Default value (SSR mode only)
148
151
 
@@ -153,8 +156,10 @@ await execute({
153
156
  **Execute Options (Client mode only):**
154
157
  - `id?: string | number` - Single resource ID
155
158
  - `ids?: (string | number)[]` - Batch operation IDs (PATCH/DELETE)
156
- - `files?: FormData[]` - Batch file upload (POST)
159
+ - `files?: FormData[]` - Array of FormData objects for batch upload (POST)
157
160
  - `body?: any` - Override request body
161
+ - `batchSize?: number` - Override batch size for this execution
162
+ - `concurrent?: number` - Override concurrent limit for this execution
158
163
 
159
164
  ### `useEnfyraAuth()`
160
165
 
@@ -178,7 +183,7 @@ await fetchUser() // Refresh user data
178
183
  ### Batch Operations
179
184
 
180
185
  ```typescript
181
- // Batch delete multiple items
186
+ // Basic batch delete - unlimited parallel requests
182
187
  const { execute: deleteItems } = useEnfyraApi('/items', {
183
188
  method: 'delete',
184
189
  errorContext: 'Delete Items'
@@ -186,14 +191,50 @@ const { execute: deleteItems } = useEnfyraApi('/items', {
186
191
 
187
192
  await deleteItems({ ids: ['1', '2', '3'] });
188
193
 
189
- // Batch file upload
190
- const { execute: uploadFiles } = useEnfyraApi('/files', {
191
- method: 'post',
194
+ // Advanced batch operations with concurrency control
195
+ const { execute: deleteMany } = useEnfyraApi('/users', {
196
+ method: 'delete',
197
+ batchSize: 10, // Process 10 items at a time
198
+ concurrent: 3, // Max 3 requests simultaneously
199
+ onError: (error, context) => toast.error(`${context}: ${error.message}`)
200
+ });
201
+
202
+ // Delete 100 users in controlled batches
203
+ await deleteMany({ ids: Array.from({length: 100}, (_, i) => `user-${i}`) });
204
+
205
+ // Override batch settings per execution
206
+ const { execute: updateUsers } = useEnfyraApi('/users', {
207
+ method: 'patch',
208
+ batchSize: 20, // Default: 20 per batch
209
+ concurrent: 5 // Default: 5 concurrent
210
+ });
211
+
212
+ // This execution uses different settings
213
+ await updateUsers({
214
+ ids: largeUserList,
215
+ body: { status: 'active' },
216
+ batchSize: 50, // Override: 50 per batch for this call
217
+ concurrent: 10 // Override: 10 concurrent for this call
218
+ });
219
+
220
+ // Batch file upload with progress control
221
+ const { execute: uploadFiles } = useEnfyraApi('/file_definition', {
222
+ method: 'post',
223
+ batchSize: 5, // Upload 5 files at a time
224
+ concurrent: 2, // Max 2 uploads simultaneously
192
225
  errorContext: 'Upload Files'
193
226
  });
194
227
 
228
+ // Convert files to FormData array (matches enfyra_app pattern)
229
+ const formDataArray = selectedFiles.map(file => {
230
+ const formData = new FormData();
231
+ formData.append('file', file);
232
+ formData.append('folder', folderId || 'null');
233
+ return formData;
234
+ });
235
+
195
236
  await uploadFiles({
196
- files: [formData1, formData2, formData3]
237
+ files: formDataArray // Array of FormData objects
197
238
  });
198
239
  ```
199
240
 
@@ -16,7 +16,7 @@ function handleError(error, context, customHandler) {
16
16
  return apiError;
17
17
  }
18
18
  export function useEnfyraApi(path, opts = {}) {
19
- const { method = "get", body, query, errorContext, onError, ssr, key } = opts;
19
+ const { method = "get", body, query, errorContext, onError, ssr, key, batchSize, concurrent } = opts;
20
20
  if (ssr) {
21
21
  const config = useRuntimeConfig().public.enfyraSDK;
22
22
  const basePath = (typeof path === "function" ? path() : path).replace(/^\/?api\/?/, "").replace(/^\/+/, "");
@@ -65,12 +65,38 @@ export function useEnfyraApi(path, opts = {}) {
65
65
  const basePath = (typeof path === "function" ? path() : path).replace(/^\/?api\/?/, "").replace(/^\/+/, "");
66
66
  const finalBody = executeOpts?.body || unref(body);
67
67
  const finalQuery = unref(query);
68
+ const effectiveBatchSize = executeOpts?.batchSize ?? batchSize;
69
+ const effectiveConcurrent = executeOpts?.concurrent ?? concurrent;
68
70
  const buildPath = (...segments) => {
69
71
  return segments.filter(Boolean).join("/");
70
72
  };
71
73
  const fullBaseURL = apiUrl + (apiPrefix || "");
74
+ async function processBatch(items, processor) {
75
+ const results = [];
76
+ if (!effectiveBatchSize && !effectiveConcurrent) {
77
+ const promises = items.map(processor);
78
+ return await Promise.all(promises);
79
+ }
80
+ const chunks = effectiveBatchSize ? Array.from(
81
+ { length: Math.ceil(items.length / effectiveBatchSize) },
82
+ (_, i) => items.slice(i * effectiveBatchSize, i * effectiveBatchSize + effectiveBatchSize)
83
+ ) : [items];
84
+ for (const chunk of chunks) {
85
+ if (effectiveConcurrent && chunk.length > effectiveConcurrent) {
86
+ for (let i = 0; i < chunk.length; i += effectiveConcurrent) {
87
+ const batch = chunk.slice(i, i + effectiveConcurrent);
88
+ const batchResults = await Promise.all(batch.map(processor));
89
+ results.push(...batchResults);
90
+ }
91
+ } else {
92
+ const chunkResults = await Promise.all(chunk.map(processor));
93
+ results.push(...chunkResults);
94
+ }
95
+ }
96
+ return results;
97
+ }
72
98
  if (!opts.disableBatch && executeOpts?.ids && executeOpts.ids.length > 0 && (method.toLowerCase() === "patch" || method.toLowerCase() === "delete")) {
73
- const promises = executeOpts.ids.map(async (id) => {
99
+ const responses = await processBatch(executeOpts.ids, async (id) => {
74
100
  const finalPath2 = buildPath(basePath, id);
75
101
  return $fetch(finalPath2, {
76
102
  baseURL: fullBaseURL,
@@ -80,12 +106,11 @@ export function useEnfyraApi(path, opts = {}) {
80
106
  query: finalQuery
81
107
  });
82
108
  });
83
- const responses = await Promise.all(promises);
84
109
  data.value = responses;
85
110
  return responses;
86
111
  }
87
112
  if (!opts.disableBatch && method.toLowerCase() === "post" && executeOpts?.files && Array.isArray(executeOpts.files) && executeOpts.files.length > 0) {
88
- const promises = executeOpts.files.map(async (fileObj) => {
113
+ const responses = await processBatch(executeOpts.files, async (fileObj) => {
89
114
  return $fetch(basePath, {
90
115
  baseURL: fullBaseURL,
91
116
  method,
@@ -95,7 +120,6 @@ export function useEnfyraApi(path, opts = {}) {
95
120
  query: finalQuery
96
121
  });
97
122
  });
98
- const responses = await Promise.all(promises);
99
123
  data.value = responses;
100
124
  return responses;
101
125
  }
package/dist/index.d.ts CHANGED
@@ -17,6 +17,10 @@ export interface ApiOptions<T> {
17
17
  errorContext?: string;
18
18
  onError?: (error: ApiError, context?: string) => void;
19
19
  disableBatch?: boolean;
20
+ /** Batch size for chunking large operations (default: no limit) */
21
+ batchSize?: number;
22
+ /** Maximum concurrent requests (default: no limit) */
23
+ concurrent?: number;
20
24
  default?: () => T;
21
25
  /** Enable SSR with useFetch instead of $fetch */
22
26
  ssr?: boolean;
@@ -46,16 +50,22 @@ export interface UseEnfyraApiSSRReturn<T> extends AsyncData<T | null, ApiError>
46
50
  error: Ref<ApiError | null>;
47
51
  refresh: () => Promise<void>;
48
52
  }
53
+ export interface ExecuteOptions {
54
+ body?: any;
55
+ id?: string | number;
56
+ ids?: (string | number)[];
57
+ /** Array of FormData objects for batch upload */
58
+ files?: FormData[];
59
+ /** Override batch size for this specific execution */
60
+ batchSize?: number;
61
+ /** Override concurrent limit for this specific execution */
62
+ concurrent?: number;
63
+ }
49
64
  export interface UseEnfyraApiClientReturn<T> {
50
65
  data: Ref<T | null>;
51
66
  error: Ref<ApiError | null>;
52
67
  pending: Ref<boolean>;
53
- execute: (executeOpts?: {
54
- body?: any;
55
- id?: string | number;
56
- ids?: (string | number)[];
57
- files?: any[];
58
- }) => Promise<T | T[] | null>;
68
+ execute: (executeOpts?: ExecuteOptions) => Promise<T | T[] | null>;
59
69
  }
60
70
  export * from './auth';
61
71
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/types/index.ts"],"names":[],"mappings":"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"}
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@enfyra/sdk-nuxt",
3
3
  "configKey": "enfyraSDK",
4
- "version": "0.2.1",
4
+ "version": "0.2.2",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enfyra/sdk-nuxt",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Nuxt SDK for Enfyra CMS",
5
5
  "repository": {
6
6
  "type": "git",
@@ -3,6 +3,7 @@ import type {
3
3
  ApiOptions,
4
4
  ApiError,
5
5
  BackendErrorExtended,
6
+ ExecuteOptions,
6
7
  UseEnfyraApiSSRReturn,
7
8
  UseEnfyraApiClientReturn,
8
9
  } from "../types";
@@ -46,7 +47,7 @@ export function useEnfyraApi<T = any>(
46
47
  path: (() => string) | string,
47
48
  opts: ApiOptions<T> = {}
48
49
  ): UseEnfyraApiSSRReturn<T> | UseEnfyraApiClientReturn<T> {
49
- const { method = "get", body, query, errorContext, onError, ssr, key } = opts;
50
+ const { method = "get", body, query, errorContext, onError, ssr, key, batchSize, concurrent } = opts;
50
51
 
51
52
  // SSR mode - use useFetch
52
53
  if (ssr) {
@@ -101,12 +102,7 @@ export function useEnfyraApi<T = any>(
101
102
  const error = ref<ApiError | null>(null);
102
103
  const pending = ref(false);
103
104
 
104
- const execute = async (executeOpts?: {
105
- body?: any;
106
- id?: string | number;
107
- ids?: (string | number)[];
108
- files?: any[];
109
- }) => {
105
+ const execute = async (executeOpts?: ExecuteOptions) => {
110
106
  pending.value = true;
111
107
  error.value = null;
112
108
 
@@ -120,6 +116,10 @@ export function useEnfyraApi<T = any>(
120
116
  .replace(/^\/+/, ""); // Remove leading slashes
121
117
  const finalBody = executeOpts?.body || unref(body);
122
118
  const finalQuery = unref(query);
119
+
120
+ // Use executeOpts overrides if provided, otherwise fall back to options
121
+ const effectiveBatchSize = executeOpts?.batchSize ?? batchSize;
122
+ const effectiveConcurrent = executeOpts?.concurrent ?? concurrent;
123
123
 
124
124
  // Helper function to build clean path
125
125
  const buildPath = (...segments: (string | number)[]): string => {
@@ -129,6 +129,44 @@ export function useEnfyraApi<T = any>(
129
129
  // Build full base URL with prefix
130
130
  const fullBaseURL = apiUrl + (apiPrefix || "");
131
131
 
132
+ // Helper function for batch processing with chunking and concurrency control
133
+ async function processBatch<T>(
134
+ items: any[],
135
+ processor: (item: any) => Promise<T>
136
+ ): Promise<T[]> {
137
+ const results: T[] = [];
138
+
139
+ // If no limits, process all at once (current behavior)
140
+ if (!effectiveBatchSize && !effectiveConcurrent) {
141
+ const promises = items.map(processor);
142
+ return await Promise.all(promises);
143
+ }
144
+
145
+ // Chunk items by batchSize
146
+ const chunks = effectiveBatchSize ?
147
+ Array.from({ length: Math.ceil(items.length / effectiveBatchSize) }, (_, i) =>
148
+ items.slice(i * effectiveBatchSize, i * effectiveBatchSize + effectiveBatchSize)
149
+ ) : [items];
150
+
151
+ // Process each chunk
152
+ for (const chunk of chunks) {
153
+ if (effectiveConcurrent && chunk.length > effectiveConcurrent) {
154
+ // Process chunk with concurrency limit
155
+ for (let i = 0; i < chunk.length; i += effectiveConcurrent) {
156
+ const batch = chunk.slice(i, i + effectiveConcurrent);
157
+ const batchResults = await Promise.all(batch.map(processor));
158
+ results.push(...batchResults);
159
+ }
160
+ } else {
161
+ // Process entire chunk at once
162
+ const chunkResults = await Promise.all(chunk.map(processor));
163
+ results.push(...chunkResults);
164
+ }
165
+ }
166
+
167
+ return results;
168
+ }
169
+
132
170
  // Batch operation with multiple IDs (only for patch and delete)
133
171
  if (
134
172
  !opts.disableBatch &&
@@ -136,7 +174,7 @@ export function useEnfyraApi<T = any>(
136
174
  executeOpts.ids.length > 0 &&
137
175
  (method.toLowerCase() === "patch" || method.toLowerCase() === "delete")
138
176
  ) {
139
- const promises = executeOpts.ids.map(async (id) => {
177
+ const responses = await processBatch(executeOpts.ids, async (id) => {
140
178
  const finalPath = buildPath(basePath, id);
141
179
  return $fetch<T>(finalPath, {
142
180
  baseURL: fullBaseURL,
@@ -147,7 +185,6 @@ export function useEnfyraApi<T = any>(
147
185
  });
148
186
  });
149
187
 
150
- const responses = await Promise.all(promises);
151
188
  data.value = responses as T;
152
189
  return responses;
153
190
  }
@@ -160,7 +197,7 @@ export function useEnfyraApi<T = any>(
160
197
  Array.isArray(executeOpts.files) &&
161
198
  executeOpts.files.length > 0
162
199
  ) {
163
- const promises = executeOpts.files.map(async (fileObj: any) => {
200
+ const responses = await processBatch(executeOpts.files, async (fileObj: any) => {
164
201
  return $fetch<T>(basePath, {
165
202
  baseURL: fullBaseURL,
166
203
  method: method as any,
@@ -170,7 +207,6 @@ export function useEnfyraApi<T = any>(
170
207
  });
171
208
  });
172
209
 
173
- const responses = await Promise.all(promises);
174
210
  data.value = responses as T;
175
211
  return responses;
176
212
  }
@@ -19,6 +19,10 @@ export interface ApiOptions<T> {
19
19
  errorContext?: string;
20
20
  onError?: (error: ApiError, context?: string) => void;
21
21
  disableBatch?: boolean;
22
+ /** Batch size for chunking large operations (default: no limit) */
23
+ batchSize?: number;
24
+ /** Maximum concurrent requests (default: no limit) */
25
+ concurrent?: number;
22
26
  default?: () => T;
23
27
  /** Enable SSR with useFetch instead of $fetch */
24
28
  ssr?: boolean;
@@ -54,17 +58,25 @@ export interface UseEnfyraApiSSRReturn<T> extends AsyncData<T | null, ApiError>
54
58
  refresh: () => Promise<void>;
55
59
  }
56
60
 
61
+ // Execute options interface
62
+ export interface ExecuteOptions {
63
+ body?: any;
64
+ id?: string | number;
65
+ ids?: (string | number)[];
66
+ /** Array of FormData objects for batch upload */
67
+ files?: FormData[];
68
+ /** Override batch size for this specific execution */
69
+ batchSize?: number;
70
+ /** Override concurrent limit for this specific execution */
71
+ concurrent?: number;
72
+ }
73
+
57
74
  // Client Mode return type
58
75
  export interface UseEnfyraApiClientReturn<T> {
59
76
  data: Ref<T | null>;
60
77
  error: Ref<ApiError | null>;
61
78
  pending: Ref<boolean>;
62
- execute: (executeOpts?: {
63
- body?: any;
64
- id?: string | number;
65
- ids?: (string | number)[];
66
- files?: any[];
67
- }) => Promise<T | T[] | null>;
79
+ execute: (executeOpts?: ExecuteOptions) => Promise<T | T[] | null>;
68
80
  }
69
81
 
70
82