@adminforth/upload 2.4.1 → 2.5.0

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/build.log CHANGED
@@ -11,5 +11,5 @@ custom/preview.vue
11
11
  custom/tsconfig.json
12
12
  custom/uploader.vue
13
13
 
14
- sent 48,314 bytes received 134 bytes 96,896.00 bytes/sec
15
- total size is 47,825 speedup is 0.99
14
+ sent 49,208 bytes received 134 bytes 98,684.00 bytes/sec
15
+ total size is 48,726 speedup is 0.99
@@ -248,7 +248,6 @@ onMounted(async () => {
248
248
 
249
249
  if (resp?.files?.length) {
250
250
  attachmentFiles.value = resp.files;
251
- console.log('attachmentFiles', attachmentFiles.value);
252
251
  }
253
252
  } catch (err) {
254
253
  console.error('Failed to fetch attachment files', err);
@@ -337,7 +336,7 @@ async function generateImages() {
337
336
  let error = null;
338
337
  try {
339
338
  resp = await callAdminForthApi({
340
- path: `/plugin/${props.meta.pluginInstanceId}/generate_images`,
339
+ path: `/plugin/${props.meta.pluginInstanceId}/create-image-generation-job`,
341
340
  method: 'POST',
342
341
  body: {
343
342
  prompt: prompt.value,
@@ -346,16 +345,13 @@ async function generateImages() {
346
345
  });
347
346
  } catch (e) {
348
347
  console.error(e);
349
- } finally {
350
- clearInterval(ticker);
351
- loadingTimer.value = null;
352
- loading.value = false;
353
348
  }
349
+
354
350
  if (resp?.error) {
355
351
  error = resp.error;
356
352
  }
357
353
  if (!resp) {
358
- error = $t('Error generating images, something went wrong');
354
+ error = $t('Error creating image generation job');
359
355
  }
360
356
 
361
357
  if (error) {
@@ -371,11 +367,53 @@ async function generateImages() {
371
367
  return;
372
368
  }
373
369
 
370
+ const jobId = resp.jobId;
371
+ let jobStatus = null;
372
+ let jobResponse = null;
373
+ do {
374
+ jobResponse = await callAdminForthApi({
375
+ path: `/plugin/${props.meta.pluginInstanceId}/get-image-generation-job-status`,
376
+ method: 'POST',
377
+ body: { jobId },
378
+ });
379
+ if (jobResponse?.error) {
380
+ error = jobResponse.error;
381
+ break;
382
+ };
383
+ jobStatus = jobResponse?.job?.status;
384
+ if (jobStatus === 'failed') {
385
+ error = jobResponse?.job?.error || $t('Image generation job failed');
386
+ }
387
+ if (jobStatus === 'timeout') {
388
+ error = jobResponse?.job?.error || $t('Image generation job timeout');
389
+ }
390
+ await new Promise((resolve) => setTimeout(resolve, 2000));
391
+ } while (jobStatus === 'in_progress')
392
+
393
+ if (error) {
394
+ adminforth.alert({
395
+ message: error,
396
+ variant: 'danger',
397
+ timeout: 'unlimited',
398
+ });
399
+ clearInterval(ticker);
400
+ loadingTimer.value = null;
401
+ loading.value = false;
402
+ return;
403
+ }
404
+
405
+ const respImages = jobResponse?.job?.images || [];
406
+
374
407
  images.value = [
375
408
  ...images.value,
376
- ...resp.images,
409
+ ...respImages,
377
410
  ];
378
411
 
412
+ clearInterval(ticker);
413
+ loadingTimer.value = null;
414
+ loading.value = false;
415
+
416
+
379
417
  // images.value = [
380
418
  // 'https://via.placeholder.com/600x400?text=Image+1',
381
419
  // 'https://via.placeholder.com/600x400?text=Image+2',
@@ -386,7 +424,6 @@ async function generateImages() {
386
424
  caurosel.value = new Carousel(
387
425
  document.getElementById('gallery'),
388
426
  images.value.map((img, index) => {
389
- console.log('mapping image', img, index);
390
427
  return {
391
428
  image: img,
392
429
  el: document.getElementById('gallery').querySelector(`[data-carousel-item]:nth-child(${index + 1})`),
@@ -248,7 +248,6 @@ onMounted(async () => {
248
248
 
249
249
  if (resp?.files?.length) {
250
250
  attachmentFiles.value = resp.files;
251
- console.log('attachmentFiles', attachmentFiles.value);
252
251
  }
253
252
  } catch (err) {
254
253
  console.error('Failed to fetch attachment files', err);
@@ -337,7 +336,7 @@ async function generateImages() {
337
336
  let error = null;
338
337
  try {
339
338
  resp = await callAdminForthApi({
340
- path: `/plugin/${props.meta.pluginInstanceId}/generate_images`,
339
+ path: `/plugin/${props.meta.pluginInstanceId}/create-image-generation-job`,
341
340
  method: 'POST',
342
341
  body: {
343
342
  prompt: prompt.value,
@@ -346,16 +345,13 @@ async function generateImages() {
346
345
  });
347
346
  } catch (e) {
348
347
  console.error(e);
349
- } finally {
350
- clearInterval(ticker);
351
- loadingTimer.value = null;
352
- loading.value = false;
353
348
  }
349
+
354
350
  if (resp?.error) {
355
351
  error = resp.error;
356
352
  }
357
353
  if (!resp) {
358
- error = $t('Error generating images, something went wrong');
354
+ error = $t('Error creating image generation job');
359
355
  }
360
356
 
361
357
  if (error) {
@@ -371,11 +367,53 @@ async function generateImages() {
371
367
  return;
372
368
  }
373
369
 
370
+ const jobId = resp.jobId;
371
+ let jobStatus = null;
372
+ let jobResponse = null;
373
+ do {
374
+ jobResponse = await callAdminForthApi({
375
+ path: `/plugin/${props.meta.pluginInstanceId}/get-image-generation-job-status`,
376
+ method: 'POST',
377
+ body: { jobId },
378
+ });
379
+ if (jobResponse?.error) {
380
+ error = jobResponse.error;
381
+ break;
382
+ };
383
+ jobStatus = jobResponse?.job?.status;
384
+ if (jobStatus === 'failed') {
385
+ error = jobResponse?.job?.error || $t('Image generation job failed');
386
+ }
387
+ if (jobStatus === 'timeout') {
388
+ error = jobResponse?.job?.error || $t('Image generation job timeout');
389
+ }
390
+ await new Promise((resolve) => setTimeout(resolve, 2000));
391
+ } while (jobStatus === 'in_progress')
392
+
393
+ if (error) {
394
+ adminforth.alert({
395
+ message: error,
396
+ variant: 'danger',
397
+ timeout: 'unlimited',
398
+ });
399
+ clearInterval(ticker);
400
+ loadingTimer.value = null;
401
+ loading.value = false;
402
+ return;
403
+ }
404
+
405
+ const respImages = jobResponse?.job?.images || [];
406
+
374
407
  images.value = [
375
408
  ...images.value,
376
- ...resp.images,
409
+ ...respImages,
377
410
  ];
378
411
 
412
+ clearInterval(ticker);
413
+ loadingTimer.value = null;
414
+ loading.value = false;
415
+
416
+
379
417
  // images.value = [
380
418
  // 'https://via.placeholder.com/600x400?text=Image+1',
381
419
  // 'https://via.placeholder.com/600x400?text=Image+2',
@@ -386,7 +424,6 @@ async function generateImages() {
386
424
  caurosel.value = new Carousel(
387
425
  document.getElementById('gallery'),
388
426
  images.value.map((img, index) => {
389
- console.log('mapping image', img, index);
390
427
  return {
391
428
  image: img,
392
429
  el: document.getElementById('gallery').querySelector(`[data-carousel-item]:nth-child(${index + 1})`),
package/dist/index.js CHANGED
@@ -10,9 +10,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { AdminForthPlugin, Filters, suggestIfTypo } from "adminforth";
11
11
  import { Readable } from "stream";
12
12
  import { RateLimiter } from "adminforth";
13
+ import { randomUUID } from "crypto";
13
14
  import { interpretResource } from 'adminforth';
14
15
  import { ActionCheckSource } from 'adminforth';
15
16
  const ADMINFORTH_NOT_YET_USED_TAG = 'adminforth-candidate-for-cleanup';
17
+ const jobs = new Map();
16
18
  export default class UploadPlugin extends AdminForthPlugin {
17
19
  constructor(options) {
18
20
  var _a, _b, _c;
@@ -25,6 +27,70 @@ export default class UploadPlugin extends AdminForthPlugin {
25
27
  this.rateLimiter = new RateLimiter((_c = this.options.generation.rateLimit) === null || _c === void 0 ? void 0 : _c.limit);
26
28
  }
27
29
  }
30
+ generateImages(jobId, prompt, recordId, adminUser, headers) {
31
+ return __awaiter(this, void 0, void 0, function* () {
32
+ var _a, _b;
33
+ if ((_a = this.options.generation.rateLimit) === null || _a === void 0 ? void 0 : _a.limit) {
34
+ // rate limit
35
+ // const { error } = RateLimiter.checkRateLimit(
36
+ // this.pluginInstanceId,
37
+ // this.options.generation.rateLimit?.limit,
38
+ // this.adminforth.auth.getClientIp(headers),
39
+ // );
40
+ if (!(yield this.rateLimiter.consume(`${this.pluginInstanceId}-${this.adminforth.auth.getClientIp(headers)}`))) {
41
+ jobs.set(jobId, { status: "failed", error: this.options.generation.rateLimit.errorMessage });
42
+ return { error: this.options.generation.rateLimit.errorMessage };
43
+ }
44
+ }
45
+ let attachmentFiles = [];
46
+ if (this.options.generation.attachFiles) {
47
+ // TODO - does it require additional allowed action to check this record id has access to get the image?
48
+ // or should we mention in docs that user should do validation in method itself
49
+ const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ((_b = this.resourceConfig.columns.find(c => c.primaryKey)) === null || _b === void 0 ? void 0 : _b.name, recordId)]);
50
+ if (!record) {
51
+ return { error: `Record with id ${recordId} not found` };
52
+ }
53
+ attachmentFiles = yield this.options.generation.attachFiles({ record, adminUser });
54
+ // if files is not array, make it array
55
+ if (!Array.isArray(attachmentFiles)) {
56
+ attachmentFiles = [attachmentFiles];
57
+ }
58
+ }
59
+ let error = undefined;
60
+ const STUB_MODE = false;
61
+ const images = yield Promise.all((new Array(this.options.generation.countToGenerate)).fill(0).map(() => __awaiter(this, void 0, void 0, function* () {
62
+ if (STUB_MODE) {
63
+ yield new Promise((resolve) => setTimeout(resolve, 2000));
64
+ return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
65
+ }
66
+ const start = +new Date();
67
+ let resp;
68
+ try {
69
+ resp = yield this.options.generation.adapter.generate({
70
+ prompt,
71
+ inputFiles: attachmentFiles,
72
+ n: 1,
73
+ size: this.options.generation.outputSize,
74
+ });
75
+ }
76
+ catch (e) {
77
+ error = `No response from image generation provider: ${e.message}. Please check your prompt or try again later.`;
78
+ return;
79
+ }
80
+ if (resp.error) {
81
+ console.error('Error generating image', resp.error);
82
+ error = resp.error;
83
+ return;
84
+ }
85
+ this.totalCalls++;
86
+ this.totalDuration += (+new Date() - start) / 1000;
87
+ return resp.imageURLs[0];
88
+ })));
89
+ jobs.set(jobId, { status: "completed", images, error });
90
+ return { ok: true };
91
+ });
92
+ }
93
+ ;
28
94
  instanceUniqueRepresentation(pluginOptions) {
29
95
  return `${pluginOptions.pathColumnName}`;
30
96
  }
@@ -282,66 +348,30 @@ export default class UploadPlugin extends AdminForthPlugin {
282
348
  // }'
283
349
  server.endpoint({
284
350
  method: 'POST',
285
- path: `/plugin/${this.pluginInstanceId}/generate_images`,
351
+ path: `/plugin/${this.pluginInstanceId}/create-image-generation-job`,
286
352
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
287
- var _b;
288
353
  const { prompt, recordId } = body;
289
- if (this.rateLimiter) {
290
- // rate limit
291
- // const { error } = RateLimiter.checkRateLimit(
292
- // this.pluginInstanceId,
293
- // this.options.generation.rateLimit?.limit,
294
- // this.adminforth.auth.getClientIp(headers),
295
- // );
296
- if (!(yield this.rateLimiter.consume(`${this.pluginInstanceId}-${this.adminforth.auth.getClientIp(headers)}`))) {
297
- return { error: this.options.generation.rateLimit.errorMessage };
298
- }
354
+ const jobId = randomUUID();
355
+ jobs.set(jobId, { status: "in_progress" });
356
+ this.generateImages(jobId, prompt, recordId, adminUser, headers);
357
+ setTimeout(() => jobs.delete(jobId), 1800000);
358
+ setTimeout(() => { jobs.set(jobId, { status: "timeout" }); }, 300000);
359
+ return { ok: true, jobId };
360
+ })
361
+ });
362
+ server.endpoint({
363
+ method: 'POST',
364
+ path: `/plugin/${this.pluginInstanceId}/get-image-generation-job-status`,
365
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, headers }) {
366
+ const jobId = body.jobId;
367
+ if (!jobId) {
368
+ return { error: "Can't find job id" };
299
369
  }
300
- let attachmentFiles = [];
301
- if (this.options.generation.attachFiles) {
302
- // TODO - does it require additional allowed action to check this record id has access to get the image?
303
- // or should we mention in docs that user should do validation in method itself
304
- const record = yield this.adminforth.resource(this.resourceConfig.resourceId).get([Filters.EQ((_b = this.resourceConfig.columns.find((column) => column.primaryKey)) === null || _b === void 0 ? void 0 : _b.name, recordId)]);
305
- if (!record) {
306
- return { error: `Record with id ${recordId} not found` };
307
- }
308
- attachmentFiles = yield this.options.generation.attachFiles({ record, adminUser });
309
- // if files is not array, make it array
310
- if (!Array.isArray(attachmentFiles)) {
311
- attachmentFiles = [attachmentFiles];
312
- }
370
+ const job = jobs.get(jobId);
371
+ if (!job) {
372
+ return { error: "Job not found" };
313
373
  }
314
- let error = undefined;
315
- const STUB_MODE = false;
316
- const images = yield Promise.all((new Array(this.options.generation.countToGenerate)).fill(0).map(() => __awaiter(this, void 0, void 0, function* () {
317
- if (STUB_MODE) {
318
- yield new Promise((resolve) => setTimeout(resolve, 2000));
319
- return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
320
- }
321
- const start = +new Date();
322
- let resp;
323
- try {
324
- resp = yield this.options.generation.adapter.generate({
325
- prompt,
326
- inputFiles: attachmentFiles,
327
- n: 1,
328
- size: this.options.generation.outputSize,
329
- });
330
- }
331
- catch (e) {
332
- error = `No response from image generation provider: ${e.message}. Please check your prompt or try again later.`;
333
- return;
334
- }
335
- if (resp.error) {
336
- console.error('Error generating image', resp.error);
337
- error = resp.error;
338
- return;
339
- }
340
- this.totalCalls++;
341
- this.totalDuration += (+new Date() - start) / 1000;
342
- return resp.imageURLs[0];
343
- })));
344
- return { error, images };
374
+ return { ok: true, job };
345
375
  })
346
376
  });
347
377
  server.endpoint({
package/index.ts CHANGED
@@ -3,11 +3,12 @@ import { PluginOptions } from './types.js';
3
3
  import { AdminForthPlugin, AdminForthResourceColumn, AdminForthResource, Filters, IAdminForth, IHttpServer, suggestIfTypo } from "adminforth";
4
4
  import { Readable } from "stream";
5
5
  import { RateLimiter } from "adminforth";
6
+ import { randomUUID } from "crypto";
6
7
  import { interpretResource } from 'adminforth';
7
8
  import { ActionCheckSource } from 'adminforth';
8
9
 
9
10
  const ADMINFORTH_NOT_YET_USED_TAG = 'adminforth-candidate-for-cleanup';
10
-
11
+ const jobs = new Map();
11
12
  export default class UploadPlugin extends AdminForthPlugin {
12
13
  options: PluginOptions;
13
14
 
@@ -28,10 +29,87 @@ export default class UploadPlugin extends AdminForthPlugin {
28
29
  this.totalCalls = 0;
29
30
  this.totalDuration = 0;
30
31
  if (this.options.generation?.rateLimit?.limit) {
31
- this.rateLimiter = new RateLimiter(this.options.generation.rateLimit?.limit)
32
+ this.rateLimiter = new RateLimiter(this.options.generation.rateLimit?.limit)
32
33
  }
33
34
  }
34
35
 
36
+ private async generateImages(jobId: string, prompt: string, recordId: any, adminUser: any, headers: any) {
37
+ if (this.options.generation.rateLimit?.limit) {
38
+ // rate limit
39
+ // const { error } = RateLimiter.checkRateLimit(
40
+ // this.pluginInstanceId,
41
+ // this.options.generation.rateLimit?.limit,
42
+ // this.adminforth.auth.getClientIp(headers),
43
+ // );
44
+ if (!await this.rateLimiter.consume(`${this.pluginInstanceId}-${this.adminforth.auth.getClientIp(headers)}`)) {
45
+ jobs.set(jobId, { status: "failed", error: this.options.generation.rateLimit.errorMessage });
46
+ return { error: this.options.generation.rateLimit.errorMessage };
47
+ }
48
+ }
49
+ let attachmentFiles = [];
50
+ if (this.options.generation.attachFiles) {
51
+ // TODO - does it require additional allowed action to check this record id has access to get the image?
52
+ // or should we mention in docs that user should do validation in method itself
53
+ const record = await this.adminforth.resource(this.resourceConfig.resourceId).get(
54
+ [Filters.EQ(this.resourceConfig.columns.find(c => c.primaryKey)?.name, recordId)]
55
+ );
56
+
57
+
58
+ if (!record) {
59
+ return { error: `Record with id ${recordId} not found` };
60
+ }
61
+
62
+ attachmentFiles = await this.options.generation.attachFiles({ record, adminUser });
63
+ // if files is not array, make it array
64
+ if (!Array.isArray(attachmentFiles)) {
65
+ attachmentFiles = [attachmentFiles];
66
+ }
67
+
68
+ }
69
+
70
+ let error: string | undefined = undefined;
71
+
72
+ const STUB_MODE = false;
73
+
74
+ const images = await Promise.all(
75
+ (new Array(this.options.generation.countToGenerate)).fill(0).map(async () => {
76
+ if (STUB_MODE) {
77
+ await new Promise((resolve) => setTimeout(resolve, 2000));
78
+ return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
79
+ }
80
+ const start = +new Date();
81
+ let resp;
82
+ try {
83
+ resp = await this.options.generation.adapter.generate(
84
+ {
85
+ prompt,
86
+ inputFiles: attachmentFiles,
87
+ n: 1,
88
+ size: this.options.generation.outputSize,
89
+ }
90
+ )
91
+ } catch (e: any) {
92
+ error = `No response from image generation provider: ${e.message}. Please check your prompt or try again later.`;
93
+ return;
94
+ }
95
+
96
+ if (resp.error) {
97
+ console.error('Error generating image', resp.error);
98
+ error = resp.error;
99
+ return;
100
+ }
101
+
102
+ this.totalCalls++;
103
+ this.totalDuration += (+new Date() - start) / 1000;
104
+
105
+ return resp.imageURLs[0]
106
+
107
+ })
108
+ );
109
+ jobs.set(jobId, { status: "completed", images, error });
110
+ return { ok: true };
111
+ };
112
+
35
113
  instanceUniqueRepresentation(pluginOptions: any) : string {
36
114
  return `${pluginOptions.pathColumnName}`;
37
115
  }
@@ -315,81 +393,34 @@ export default class UploadPlugin extends AdminForthPlugin {
315
393
 
316
394
  server.endpoint({
317
395
  method: 'POST',
318
- path: `/plugin/${this.pluginInstanceId}/generate_images`,
396
+ path: `/plugin/${this.pluginInstanceId}/create-image-generation-job`,
319
397
  handler: async ({ body, adminUser, headers }) => {
320
398
  const { prompt, recordId } = body;
321
- if (this.rateLimiter) {
322
- // rate limit
323
- // const { error } = RateLimiter.checkRateLimit(
324
- // this.pluginInstanceId,
325
- // this.options.generation.rateLimit?.limit,
326
- // this.adminforth.auth.getClientIp(headers),
327
- // );
328
- if (!await this.rateLimiter.consume(`${this.pluginInstanceId}-${this.adminforth.auth.getClientIp(headers)}`)) {
329
- return { error: this.options.generation.rateLimit.errorMessage };
330
- }
331
- }
332
- let attachmentFiles = [];
333
- if (this.options.generation.attachFiles) {
334
- // TODO - does it require additional allowed action to check this record id has access to get the image?
335
- // or should we mention in docs that user should do validation in method itself
336
- const record = await this.adminforth.resource(this.resourceConfig.resourceId).get(
337
- [Filters.EQ(this.resourceConfig.columns.find((column: any) => column.primaryKey)?.name, recordId)]
338
- );
339
-
340
- if (!record) {
341
- return { error: `Record with id ${recordId} not found` };
342
- }
343
-
344
- attachmentFiles = await this.options.generation.attachFiles({ record, adminUser });
345
- // if files is not array, make it array
346
- if (!Array.isArray(attachmentFiles)) {
347
- attachmentFiles = [attachmentFiles];
348
- }
349
-
350
- }
351
-
352
- let error: string | undefined = undefined;
353
-
354
- const STUB_MODE = false;
355
-
356
- const images = await Promise.all(
357
- (new Array(this.options.generation.countToGenerate)).fill(0).map(async () => {
358
- if (STUB_MODE) {
359
- await new Promise((resolve) => setTimeout(resolve, 2000));
360
- return `https://picsum.photos/200/300?random=${Math.floor(Math.random() * 1000)}`;
361
- }
362
- const start = +new Date();
363
- let resp;
364
- try {
365
- resp = await this.options.generation.adapter.generate(
366
- {
367
- prompt,
368
- inputFiles: attachmentFiles,
369
- n: 1,
370
- size: this.options.generation.outputSize,
371
- }
372
- )
373
- } catch (e: any) {
374
- error = `No response from image generation provider: ${e.message}. Please check your prompt or try again later.`;
375
- return;
376
- }
377
399
 
378
- if (resp.error) {
379
- console.error('Error generating image', resp.error);
380
- error = resp.error;
381
- return;
382
- }
400
+ const jobId = randomUUID();
401
+ jobs.set(jobId, { status: "in_progress" });
383
402
 
384
- this.totalCalls++;
385
- this.totalDuration += (+new Date() - start) / 1000;
386
-
387
- return resp.imageURLs[0]
403
+ this.generateImages(jobId, prompt, recordId, adminUser, headers);
404
+ setTimeout(() => jobs.delete(jobId), 1_800_000);
405
+ setTimeout(() => {jobs.set(jobId, { status: "timeout" });}, 300_000);
388
406
 
389
- })
390
- );
407
+ return { ok: true, jobId };
408
+ }
409
+ });
391
410
 
392
- return { error, images };
411
+ server.endpoint({
412
+ method: 'POST',
413
+ path: `/plugin/${this.pluginInstanceId}/get-image-generation-job-status`,
414
+ handler: async ({ body, adminUser, headers }) => {
415
+ const jobId = body.jobId;
416
+ if (!jobId) {
417
+ return { error: "Can't find job id" };
418
+ }
419
+ const job = jobs.get(jobId);
420
+ if (!job) {
421
+ return { error: "Job not found" };
422
+ }
423
+ return { ok: true, job };
393
424
  }
394
425
  });
395
426
 
@@ -451,5 +482,6 @@ export default class UploadPlugin extends AdminForthPlugin {
451
482
  });
452
483
 
453
484
  }
485
+
454
486
 
455
487
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/upload",
3
- "version": "2.4.1",
3
+ "version": "2.5.0",
4
4
  "description": "Plugin for uploading files for adminforth",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",