@adminforth/import-export 1.4.8 → 1.4.10

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.
@@ -17,18 +17,18 @@ steps:
17
17
  - infisical export --domain https://vault.devforth.io/api --format=dotenv-export --env="prod" > /woodpecker/deploy.vault.env
18
18
 
19
19
  build:
20
- image: node:20
20
+ image: devforth/node20-pnpm:latest
21
21
  when:
22
22
  - event: push
23
23
  commands:
24
24
  - apt update && apt install -y rsync
25
25
  - . /woodpecker/deploy.vault.env
26
- - npm clean-install
26
+ - pnpm install
27
27
  - /bin/bash ./.woodpecker/buildRelease.sh
28
28
  - npm audit signatures
29
29
 
30
30
  release:
31
- image: node:20
31
+ image: devforth/node20-pnpm:latest
32
32
  when:
33
33
  - event:
34
34
  - push
@@ -36,7 +36,7 @@ steps:
36
36
  - main
37
37
  commands:
38
38
  - . /woodpecker/deploy.vault.env
39
- - npx semantic-release
39
+ - pnpm exec semantic-release
40
40
 
41
41
  slack-on-failure:
42
42
  image: curlimages/curl
package/build.log CHANGED
@@ -10,5 +10,5 @@ custom/package-lock.json
10
10
  custom/package.json
11
11
  custom/tsconfig.json
12
12
 
13
- sent 16,779 bytes received 115 bytes 33,788.00 bytes/sec
14
- total size is 16,359 speedup is 0.97
13
+ sent 16,704 bytes received 115 bytes 33,638.00 bytes/sec
14
+ total size is 16,284 speedup is 0.97
@@ -54,12 +54,9 @@ async function exportCsv() {
54
54
  throw new Error(resp.error);
55
55
  }
56
56
 
57
- // Parse the CSV data to ensure proper formatting
58
- const parsedData = Papa.parse(resp.data).data;
59
-
60
57
  // Generate properly formatted CSV
61
- const csvContent = '\ufeff' + Papa.unparse(parsedData, {
62
- quotes: true, // Force quotes around all fields
58
+ const csvContent = '\ufeff' + Papa.unparse(resp.data, {
59
+ quotes: resp.columnsToForceQuote, // Force quotes only certain columns (!!!not all this breaks BI/Excel tasks)
63
60
  quoteChar: '"',
64
61
  escapeChar: '"',
65
62
  });
@@ -165,6 +165,7 @@ async function importCsv() {
165
165
  Papa.parse(text, {
166
166
  header: true,
167
167
  skipEmptyLines: true,
168
+ // dynamicTyping: true, - bad option becaue it tries to parse "1" -> int even if the column is string
168
169
 
169
170
  complete: async (results) => {
170
171
  if (results.errors.length > 0) {
@@ -214,12 +215,7 @@ async function importCsv() {
214
215
  reader.readAsText(file);
215
216
  };
216
217
  }
217
- function handleImportClick() {
218
- console.log('handleImportClick', checkProgress.value);
219
- if (!checkProgress.value) {
220
- importCsv();
221
- }
222
- }
218
+
223
219
 
224
220
  function click() {
225
221
  importCsv();
@@ -54,12 +54,9 @@ async function exportCsv() {
54
54
  throw new Error(resp.error);
55
55
  }
56
56
 
57
- // Parse the CSV data to ensure proper formatting
58
- const parsedData = Papa.parse(resp.data).data;
59
-
60
57
  // Generate properly formatted CSV
61
- const csvContent = '\ufeff' + Papa.unparse(parsedData, {
62
- quotes: true, // Force quotes around all fields
58
+ const csvContent = '\ufeff' + Papa.unparse(resp.data, {
59
+ quotes: resp.columnsToForceQuote, // Force quotes only certain columns (!!!not all this breaks BI/Excel tasks)
63
60
  quoteChar: '"',
64
61
  escapeChar: '"',
65
62
  });
@@ -165,6 +165,7 @@ async function importCsv() {
165
165
  Papa.parse(text, {
166
166
  header: true,
167
167
  skipEmptyLines: true,
168
+ // dynamicTyping: true, - bad option becaue it tries to parse "1" -> int even if the column is string
168
169
 
169
170
  complete: async (results) => {
170
171
  if (results.errors.length > 0) {
@@ -214,12 +215,7 @@ async function importCsv() {
214
215
  reader.readAsText(file);
215
216
  };
216
217
  }
217
- function handleImportClick() {
218
- console.log('handleImportClick', checkProgress.value);
219
- if (!checkProgress.value) {
220
- importCsv();
221
- }
222
- }
218
+
223
219
 
224
220
  function click() {
225
221
  importCsv();
package/dist/index.js CHANGED
@@ -64,39 +64,23 @@ export default class ImportExport extends AdminForthPlugin {
64
64
  sort,
65
65
  getTotals: true,
66
66
  });
67
- // csv export
67
+ // prepare data for PapaParse unparse
68
68
  const columns = this.resourceConfig.columns.filter((col) => !col.virtual);
69
- const escapeCSV = (value, column) => {
70
- if (value === null || value === undefined) {
71
- return '""';
72
- }
73
- if ((column === null || column === void 0 ? void 0 : column.type) === AdminForthDataTypes.FLOAT
74
- || (column === null || column === void 0 ? void 0 : column.type) === AdminForthDataTypes.INTEGER) {
75
- // no quotes for numbers
76
- return String(value);
77
- }
78
- if ((column === null || column === void 0 ? void 0 : column.type) === AdminForthDataTypes.BOOLEAN) {
79
- // no quotes for boolean values
80
- if (value === true) {
81
- return 'true';
82
- }
83
- else if (value === false) {
84
- return 'false';
85
- }
86
- }
87
- const str = String(value);
88
- if (str.includes(',') || str.includes('"') || str.includes('\n')) {
89
- return `"${str.replace(/"/g, '""')}"`;
90
- }
91
- return `"${str}"`;
69
+ const columnsToForceQuote = columns.map(col => {
70
+ return col.type !== AdminForthDataTypes.FLOAT
71
+ && col.type !== AdminForthDataTypes.INTEGER
72
+ && col.type !== AdminForthDataTypes.BOOLEAN;
73
+ });
74
+ const fields = columns.map((col) => col.name);
75
+ const rows = data.data.map((row) => {
76
+ return columns.map((col) => row[col.name]);
77
+ });
78
+ return {
79
+ data: { fields, data: rows },
80
+ columnsToForceQuote,
81
+ exportedCount: data.total,
82
+ ok: true
92
83
  };
93
- let csv = data.data.map((row) => {
94
- return columns.map((col) => escapeCSV(row[col.name], col)).join(',');
95
- }).join('\n');
96
- // add headers
97
- const headers = columns.map((col) => escapeCSV(col.name)).join(',');
98
- csv = `${headers}\n${csv}`;
99
- return { data: csv, exportedCount: data.total, ok: true };
100
84
  })
101
85
  });
102
86
  server.endpoint({
@@ -105,28 +89,14 @@ export default class ImportExport extends AdminForthPlugin {
105
89
  noAuth: true,
106
90
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body }) {
107
91
  const { data } = body;
108
- const rows = [];
109
- const columns = Object.keys(data);
110
- // check column names are valid
111
- const errors = [];
112
- columns.forEach((col) => {
113
- if (!this.resourceConfig.columns.some((c) => c.name === col)) {
114
- const similar = suggestIfTypo(this.resourceConfig.columns.map((c) => c.name), col);
115
- errors.push(`Column '${col}' defined in CSV not found in resource '${this.resourceConfig.resourceId}'. ${similar ? `If you mean '${similar}', rename it in CSV` : 'If column is in database but not in resource configuration, add it with showIn:[]'}`);
116
- }
117
- });
92
+ const columns = this.getColumnNames(data);
93
+ const { errors, resourceColumns } = this.validateColumns(columns);
118
94
  if (errors.length > 0) {
119
95
  return { ok: false, errors };
120
96
  }
121
97
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
122
- const columnValues = Object.values(data);
123
- for (let i = 0; i < columnValues[0].length; i++) {
124
- const row = {};
125
- for (let j = 0; j < columns.length; j++) {
126
- row[columns[j]] = columnValues[j][i];
127
- }
128
- rows.push(row);
129
- }
98
+ const rows = this.buildRowsFromData(data, columns, resourceColumns, { coerceTypes: true });
99
+ console.log('Prepared rows for import:', rows);
130
100
  let importedCount = 0;
131
101
  let updatedCount = 0;
132
102
  yield Promise.all(rows.map((row) => __awaiter(this, void 0, void 0, function* () {
@@ -157,28 +127,13 @@ export default class ImportExport extends AdminForthPlugin {
157
127
  noAuth: true,
158
128
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body }) {
159
129
  const { data } = body;
160
- const rows = [];
161
- const columns = Object.keys(data);
162
- // check column names are valid
163
- const errors = [];
164
- columns.forEach((col) => {
165
- if (!this.resourceConfig.columns.some((c) => c.name === col)) {
166
- const similar = suggestIfTypo(this.resourceConfig.columns.map((c) => c.name), col);
167
- errors.push(`Column '${col}' defined in CSV not found in resource '${this.resourceConfig.resourceId}'. ${similar ? `If you mean '${similar}', rename it in CSV` : 'If column is in database but not in resource configuration, add it with showIn:[]'}`);
168
- }
169
- });
130
+ const columns = this.getColumnNames(data);
131
+ const { errors, resourceColumns } = this.validateColumns(columns);
170
132
  if (errors.length > 0) {
171
133
  return { ok: false, errors };
172
134
  }
173
135
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
174
- const columnValues = Object.values(data);
175
- for (let i = 0; i < columnValues[0].length; i++) {
176
- const row = {};
177
- for (let j = 0; j < columns.length; j++) {
178
- row[columns[j]] = columnValues[j][i];
179
- }
180
- rows.push(row);
181
- }
136
+ const rows = this.buildRowsFromData(data, columns, resourceColumns, { coerceTypes: true });
182
137
  let importedCount = 0;
183
138
  yield Promise.all(rows.map((row) => __awaiter(this, void 0, void 0, function* () {
184
139
  try {
@@ -206,17 +161,10 @@ export default class ImportExport extends AdminForthPlugin {
206
161
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body }) {
207
162
  const { data } = body;
208
163
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
209
- const columns = Object.keys(data);
210
- const columnValues = Object.values(data);
211
- const rows = Array.from({ length: columnValues[0].length }, (_, i) => {
212
- const row = {};
213
- for (let j = 0; j < columns.length; j++) {
214
- row[columns[j]] = columnValues[j][i];
215
- }
216
- return row;
217
- });
164
+ const columns = this.getColumnNames(data);
165
+ const rows = this.buildRowsFromData(data, columns, undefined, { coerceTypes: false });
218
166
  const primaryKeys = rows
219
- .map(row => row[primaryKeyColumn.name])
167
+ .map(row => primaryKeyColumn ? row[primaryKeyColumn.name] : undefined)
220
168
  .filter(key => key !== undefined && key !== null && key !== '');
221
169
  const existingRecords = yield this.adminforth
222
170
  .resource(this.resourceConfig.resourceId)
@@ -234,4 +182,59 @@ export default class ImportExport extends AdminForthPlugin {
234
182
  })
235
183
  });
236
184
  }
185
+ getColumnNames(data) {
186
+ return Object.keys(data !== null && data !== void 0 ? data : {});
187
+ }
188
+ validateColumns(columns) {
189
+ const errors = [];
190
+ const resourceColumns = [];
191
+ columns.forEach((col) => {
192
+ const resourceColumn = this.resourceConfig.columns.find((c) => c.name === col);
193
+ if (!resourceColumn) {
194
+ const similar = suggestIfTypo(this.resourceConfig.columns.map((c) => c.name), col);
195
+ errors.push(`Column '${col}' defined in CSV not found in resource '${this.resourceConfig.resourceId}'. ${similar
196
+ ? `If you mean '${similar}', rename it in CSV`
197
+ : 'If column is in database but not in resource configuration, add it with showIn:[]'}`);
198
+ return;
199
+ }
200
+ resourceColumns.push(resourceColumn);
201
+ });
202
+ return { errors, resourceColumns };
203
+ }
204
+ buildRowsFromData(data, columns, resourceColumns, { coerceTypes } = { coerceTypes: true }) {
205
+ const columnValues = Object.values(data !== null && data !== void 0 ? data : {});
206
+ if (columns.length === 0 || columnValues.length === 0) {
207
+ return [];
208
+ }
209
+ const rows = [];
210
+ const rowCount = columnValues[0].length;
211
+ for (let i = 0; i < rowCount; i++) {
212
+ const row = {};
213
+ for (let j = 0; j < columns.length; j++) {
214
+ const val = columnValues[j][i];
215
+ const resourceCol = resourceColumns ? resourceColumns[j] : undefined;
216
+ row[columns[j]] = coerceTypes
217
+ ? this.coerceValue(resourceCol, val)
218
+ : val;
219
+ }
220
+ rows.push(row);
221
+ }
222
+ return rows;
223
+ }
224
+ coerceValue(resourceCol, val) {
225
+ if (!resourceCol || val === '') {
226
+ return val;
227
+ }
228
+ if ((resourceCol.type === AdminForthDataTypes.INTEGER
229
+ || resourceCol.type === AdminForthDataTypes.FLOAT)) {
230
+ return +val;
231
+ }
232
+ if (resourceCol.type === AdminForthDataTypes.BOOLEAN) {
233
+ if (typeof val === 'string') {
234
+ return val.toLowerCase() === 'true' || val === '1';
235
+ }
236
+ return val === 1 || val === true;
237
+ }
238
+ return val;
239
+ }
237
240
  }
package/index.ts CHANGED
@@ -66,42 +66,27 @@ export default class ImportExport extends AdminForthPlugin {
66
66
  getTotals: true,
67
67
  });
68
68
 
69
- // csv export
69
+ // prepare data for PapaParse unparse
70
70
  const columns = this.resourceConfig.columns.filter((col) => !col.virtual);
71
-
72
- const escapeCSV = (value: any, column?: AdminForthResourceColumn) => {
73
- if (value === null || value === undefined) {
74
- return '""';
75
- }
76
- if (column?.type === AdminForthDataTypes.FLOAT
77
- || column?.type === AdminForthDataTypes.INTEGER) {
78
- // no quotes for numbers
79
- return String(value);
80
- }
81
- if (column?.type === AdminForthDataTypes.BOOLEAN) {
82
- // no quotes for boolean values
83
- if (value === true) {
84
- return 'true';
85
- } else if (value === false) {
86
- return 'false';
87
- }
88
- }
89
- const str = String(value);
90
- if (str.includes(',') || str.includes('"') || str.includes('\n')) {
91
- return `"${str.replace(/"/g, '""')}"`;
92
- }
93
- return `"${str}"`;
94
- };
95
71
 
96
- let csv = data.data.map((row) => {
97
- return columns.map((col) => escapeCSV(row[col.name], col)).join(',');
98
- }).join('\n');
72
+ const columnsToForceQuote = columns.map(col => {
73
+ return col.type !== AdminForthDataTypes.FLOAT
74
+ && col.type !== AdminForthDataTypes.INTEGER
75
+ && col.type !== AdminForthDataTypes.BOOLEAN;
76
+ })
99
77
 
100
- // add headers
101
- const headers = columns.map((col) => escapeCSV(col.name)).join(',');
102
- csv = `${headers}\n${csv}`;
78
+ const fields = columns.map((col) => col.name);
103
79
 
104
- return { data: csv, exportedCount: data.total, ok: true };
80
+ const rows = data.data.map((row) => {
81
+ return columns.map((col) => row[col.name]);
82
+ });
83
+
84
+ return {
85
+ data: { fields, data: rows },
86
+ columnsToForceQuote,
87
+ exportedCount: data.total,
88
+ ok: true
89
+ };
105
90
  }
106
91
  });
107
92
 
@@ -111,33 +96,15 @@ export default class ImportExport extends AdminForthPlugin {
111
96
  noAuth: true,
112
97
  handler: async ({ body }) => {
113
98
  const { data } = body;
114
- const rows = [];
115
- const columns = Object.keys(data);
116
-
117
- // check column names are valid
118
- const errors: string[] = [];
119
- columns.forEach((col) => {
120
- if (!this.resourceConfig.columns.some((c) => c.name === col)) {
121
- const similar = suggestIfTypo(this.resourceConfig.columns.map((c) => c.name), col);
122
- errors.push(`Column '${col}' defined in CSV not found in resource '${this.resourceConfig.resourceId}'. ${
123
- similar ? `If you mean '${similar}', rename it in CSV` : 'If column is in database but not in resource configuration, add it with showIn:[]'}`
124
- );
125
- }
126
- });
99
+ const columns = this.getColumnNames(data);
100
+ const { errors, resourceColumns } = this.validateColumns(columns);
127
101
  if (errors.length > 0) {
128
102
  return { ok: false, errors };
129
103
  }
130
-
131
104
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
105
+ const rows = this.buildRowsFromData(data, columns, resourceColumns, { coerceTypes: true });
132
106
 
133
- const columnValues: any[] = Object.values(data);
134
- for (let i = 0; i < columnValues[0].length; i++) {
135
- const row = {};
136
- for (let j = 0; j < columns.length; j++) {
137
- row[columns[j]] = columnValues[j][i];
138
- }
139
- rows.push(row);
140
- }
107
+ console.log('Prepared rows for import:', rows);
141
108
 
142
109
  let importedCount = 0;
143
110
  let updatedCount = 0;
@@ -172,32 +139,14 @@ export default class ImportExport extends AdminForthPlugin {
172
139
  noAuth: true,
173
140
  handler: async ({ body }) => {
174
141
  const { data } = body;
175
- const rows = [];
176
- const columns = Object.keys(data);
177
-
178
- // check column names are valid
179
- const errors: string[] = [];
180
- columns.forEach((col) => {
181
- if (!this.resourceConfig.columns.some((c) => c.name === col)) {
182
- const similar = suggestIfTypo(this.resourceConfig.columns.map((c) => c.name), col);
183
- errors.push(`Column '${col}' defined in CSV not found in resource '${this.resourceConfig.resourceId}'. ${
184
- similar ? `If you mean '${similar}', rename it in CSV` : 'If column is in database but not in resource configuration, add it with showIn:[]'}`
185
- );
186
- }
187
- });
142
+ const columns = this.getColumnNames(data);
143
+ const { errors, resourceColumns } = this.validateColumns(columns);
188
144
  if (errors.length > 0) {
189
145
  return { ok: false, errors };
190
146
  }
191
147
 
192
148
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
193
- const columnValues: any[] = Object.values(data);
194
- for (let i = 0; i < columnValues[0].length; i++) {
195
- const row = {};
196
- for (let j = 0; j < columns.length; j++) {
197
- row[columns[j]] = columnValues[j][i];
198
- }
199
- rows.push(row);
200
- }
149
+ const rows = this.buildRowsFromData(data, columns, resourceColumns, { coerceTypes: true });
201
150
 
202
151
  let importedCount = 0;
203
152
 
@@ -229,19 +178,11 @@ export default class ImportExport extends AdminForthPlugin {
229
178
  handler: async ({ body }) => {
230
179
  const { data } = body as { data: Record<string, unknown[]> };
231
180
  const primaryKeyColumn = this.resourceConfig.columns.find(col => col.primaryKey);
232
- const columns = Object.keys(data);
233
- const columnValues = Object.values(data);
234
-
235
- const rows = Array.from({ length: columnValues[0].length }, (_, i) => {
236
- const row = {};
237
- for (let j = 0; j < columns.length; j++) {
238
- row[columns[j]] = columnValues[j][i];
239
- }
240
- return row;
241
- });
181
+ const columns = this.getColumnNames(data);
182
+ const rows = this.buildRowsFromData(data, columns, undefined, { coerceTypes: false });
242
183
 
243
184
  const primaryKeys = rows
244
- .map(row => row[primaryKeyColumn.name])
185
+ .map(row => primaryKeyColumn ? row[primaryKeyColumn.name] : undefined)
245
186
  .filter(key => key !== undefined && key !== null && key !== '');
246
187
 
247
188
  const existingRecords = await this.adminforth
@@ -263,4 +204,85 @@ export default class ImportExport extends AdminForthPlugin {
263
204
  });
264
205
  }
265
206
 
207
+ private getColumnNames(data: Record<string, unknown[]>): string[] {
208
+ return Object.keys(data ?? {});
209
+ }
210
+
211
+ private validateColumns(columns: string[]): {
212
+ errors: string[];
213
+ resourceColumns: AdminForthResourceColumn[];
214
+ } {
215
+ const errors: string[] = [];
216
+ const resourceColumns: AdminForthResourceColumn[] = [];
217
+
218
+ columns.forEach((col) => {
219
+ const resourceColumn = this.resourceConfig.columns.find((c) => c.name === col);
220
+ if (!resourceColumn) {
221
+ const similar = suggestIfTypo(this.resourceConfig.columns.map((c) => c.name), col);
222
+ errors.push(
223
+ `Column '${col}' defined in CSV not found in resource '${this.resourceConfig.resourceId}'. ${
224
+ similar
225
+ ? `If you mean '${similar}', rename it in CSV`
226
+ : 'If column is in database but not in resource configuration, add it with showIn:[]'
227
+ }`
228
+ );
229
+ return;
230
+ }
231
+ resourceColumns.push(resourceColumn);
232
+ });
233
+
234
+ return { errors, resourceColumns };
235
+ }
236
+
237
+ private buildRowsFromData(
238
+ data: Record<string, unknown[]>,
239
+ columns: string[],
240
+ resourceColumns?: AdminForthResourceColumn[],
241
+ { coerceTypes }: { coerceTypes: boolean } = { coerceTypes: true }
242
+ ) {
243
+ const columnValues: unknown[][] = Object.values(data ?? {});
244
+ if (columns.length === 0 || columnValues.length === 0) {
245
+ return [];
246
+ }
247
+
248
+ const rows: Record<string, unknown>[] = [];
249
+ const rowCount = columnValues[0].length;
250
+
251
+ for (let i = 0; i < rowCount; i++) {
252
+ const row: Record<string, unknown> = {};
253
+ for (let j = 0; j < columns.length; j++) {
254
+ const val = columnValues[j][i];
255
+ const resourceCol = resourceColumns ? resourceColumns[j] : undefined;
256
+ row[columns[j]] = coerceTypes
257
+ ? this.coerceValue(resourceCol, val)
258
+ : val;
259
+ }
260
+ rows.push(row);
261
+ }
262
+
263
+ return rows;
264
+ }
265
+
266
+ private coerceValue(resourceCol: AdminForthResourceColumn | undefined, val: unknown): unknown {
267
+ if (!resourceCol || val === '') {
268
+ return val;
269
+ }
270
+
271
+ if (
272
+ (resourceCol.type === AdminForthDataTypes.INTEGER
273
+ || resourceCol.type === AdminForthDataTypes.FLOAT)
274
+ ) {
275
+ return +val;
276
+ }
277
+
278
+ if (resourceCol.type === AdminForthDataTypes.BOOLEAN) {
279
+ if (typeof val === 'string') {
280
+ return val.toLowerCase() === 'true' || val === '1';
281
+ }
282
+ return val === 1 || val === true;
283
+ }
284
+
285
+ return val;
286
+ }
287
+
266
288
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/import-export",
3
- "version": "1.4.8",
3
+ "version": "1.4.10",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -9,8 +9,7 @@
9
9
  },
10
10
  "homepage": "https://adminforth.dev/docs/tutorial/Plugins/import-export/",
11
11
  "scripts": {
12
- "build": "tsc && rsync -av --exclude 'node_modules' custom dist/",
13
- "prepare": "npm link adminforth"
12
+ "build": "tsc && rsync -av --exclude 'node_modules' custom dist/"
14
13
  },
15
14
  "repository": {
16
15
  "type": "git",
@@ -26,7 +25,7 @@
26
25
  "license": "ISC",
27
26
  "description": "CSV import/export plugin for adminforth",
28
27
  "peerDependencies": {
29
- "adminforth": "next"
28
+ "adminforth": "^2.24.0"
30
29
  },
31
30
  "devDependencies": {
32
31
  "@types/node": "^22.10.7",
@@ -43,6 +42,7 @@
43
42
  [
44
43
  "semantic-release-slack-bot",
45
44
  {
45
+ "packageName": "@adminforth/import-export",
46
46
  "notifyOnSuccess": true,
47
47
  "notifyOnFail": true,
48
48
  "slackIcon": ":package:",