@wix/bex-core 2.331.0 → 2.333.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/dist/cjs/assets/locale/messages_ar.json +11 -0
- package/dist/cjs/assets/locale/messages_bg.json +11 -0
- package/dist/cjs/assets/locale/messages_ca.json +11 -0
- package/dist/cjs/assets/locale/messages_cs.json +11 -0
- package/dist/cjs/assets/locale/messages_da.json +11 -0
- package/dist/cjs/assets/locale/messages_de.json +11 -0
- package/dist/cjs/assets/locale/messages_el.json +11 -0
- package/dist/cjs/assets/locale/messages_en.json +11 -0
- package/dist/cjs/assets/locale/messages_es.json +11 -0
- package/dist/cjs/assets/locale/messages_fi.json +11 -0
- package/dist/cjs/assets/locale/messages_fr.json +11 -0
- package/dist/cjs/assets/locale/messages_he.json +11 -0
- package/dist/cjs/assets/locale/messages_hi.json +11 -0
- package/dist/cjs/assets/locale/messages_hu.json +11 -0
- package/dist/cjs/assets/locale/messages_id.json +11 -0
- package/dist/cjs/assets/locale/messages_it.json +11 -0
- package/dist/cjs/assets/locale/messages_ja.json +11 -0
- package/dist/cjs/assets/locale/messages_ko.json +11 -0
- package/dist/cjs/assets/locale/messages_lt.json +11 -0
- package/dist/cjs/assets/locale/messages_ms.json +11 -0
- package/dist/cjs/assets/locale/messages_nl.json +11 -0
- package/dist/cjs/assets/locale/messages_no.json +11 -0
- package/dist/cjs/assets/locale/messages_pl.json +11 -0
- package/dist/cjs/assets/locale/messages_pt.json +11 -0
- package/dist/cjs/assets/locale/messages_ro.json +11 -0
- package/dist/cjs/assets/locale/messages_ru.json +11 -0
- package/dist/cjs/assets/locale/messages_sk.json +11 -0
- package/dist/cjs/assets/locale/messages_sl.json +11 -0
- package/dist/cjs/assets/locale/messages_sv.json +11 -0
- package/dist/cjs/assets/locale/messages_th.json +11 -0
- package/dist/cjs/assets/locale/messages_tl.json +11 -0
- package/dist/cjs/assets/locale/messages_tr.json +11 -0
- package/dist/cjs/assets/locale/messages_uk.json +11 -0
- package/dist/cjs/assets/locale/messages_vi.json +11 -0
- package/dist/cjs/assets/locale/messages_zh.json +11 -0
- package/dist/cjs/assets/locale/version_file +1 -1
- package/dist/cjs/assets/visual-e2e-locale/messages_visuale2e.json +29 -2
- package/dist/cjs/hooks/index.js +0 -6
- package/dist/cjs/hooks/index.js.map +1 -1
- package/dist/cjs/hooks/useImport.js +3 -13
- package/dist/cjs/hooks/useImport.js.map +1 -1
- package/dist/cjs/index.js +0 -7
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/services/translations.js.map +1 -1
- package/dist/cjs/state/CustomFieldModalState.js +12 -0
- package/dist/cjs/state/CustomFieldModalState.js.map +1 -1
- package/dist/cjs/state/ImportState/ImportState.js +401 -133
- package/dist/cjs/state/ImportState/ImportState.js.map +1 -1
- package/dist/cjs/state/ImportState/__tests__/ImportState.engine.spec.js +671 -62
- package/dist/cjs/state/ImportState/__tests__/ImportState.engine.spec.js.map +1 -1
- package/dist/cjs/state/ImportState/detectFieldType.js +23 -0
- package/dist/cjs/state/ImportState/detectFieldType.js.map +1 -1
- package/dist/cjs/state/ImportState/fieldEdit.js +49 -0
- package/dist/cjs/state/ImportState/fieldEdit.js.map +1 -1
- package/dist/cjs/state/TagsState/TagsManagementState.js +17 -44
- package/dist/cjs/state/TagsState/TagsManagementState.js.map +1 -1
- package/dist/cjs/state/TagsState/TagsSearchState.js +0 -6
- package/dist/cjs/state/TagsState/TagsSearchState.js.map +1 -1
- package/dist/cjs/testkit/createInMemorySchemaSource.js +186 -0
- package/dist/cjs/testkit/createInMemorySchemaSource.js.map +1 -0
- package/dist/cjs/testkit/hubJobImportMocks.js +18 -2
- package/dist/cjs/testkit/hubJobImportMocks.js.map +1 -1
- package/dist/cjs/testkit/index.js +6 -0
- package/dist/cjs/testkit/index.js.map +1 -1
- package/dist/cjs/types/FieldsSource.js.map +1 -1
- package/dist/cjs/types/Schema.js.map +1 -1
- package/dist/cjs/types/SchemaSource.js +2 -1
- package/dist/cjs/types/SchemaSource.js.map +1 -1
- package/dist/esm/assets/locale/messages_ar.json +11 -0
- package/dist/esm/assets/locale/messages_bg.json +11 -0
- package/dist/esm/assets/locale/messages_ca.json +11 -0
- package/dist/esm/assets/locale/messages_cs.json +11 -0
- package/dist/esm/assets/locale/messages_da.json +11 -0
- package/dist/esm/assets/locale/messages_de.json +11 -0
- package/dist/esm/assets/locale/messages_el.json +11 -0
- package/dist/esm/assets/locale/messages_en.json +11 -0
- package/dist/esm/assets/locale/messages_es.json +11 -0
- package/dist/esm/assets/locale/messages_fi.json +11 -0
- package/dist/esm/assets/locale/messages_fr.json +11 -0
- package/dist/esm/assets/locale/messages_he.json +11 -0
- package/dist/esm/assets/locale/messages_hi.json +11 -0
- package/dist/esm/assets/locale/messages_hu.json +11 -0
- package/dist/esm/assets/locale/messages_id.json +11 -0
- package/dist/esm/assets/locale/messages_it.json +11 -0
- package/dist/esm/assets/locale/messages_ja.json +11 -0
- package/dist/esm/assets/locale/messages_ko.json +11 -0
- package/dist/esm/assets/locale/messages_lt.json +11 -0
- package/dist/esm/assets/locale/messages_ms.json +11 -0
- package/dist/esm/assets/locale/messages_nl.json +11 -0
- package/dist/esm/assets/locale/messages_no.json +11 -0
- package/dist/esm/assets/locale/messages_pl.json +11 -0
- package/dist/esm/assets/locale/messages_pt.json +11 -0
- package/dist/esm/assets/locale/messages_ro.json +11 -0
- package/dist/esm/assets/locale/messages_ru.json +11 -0
- package/dist/esm/assets/locale/messages_sk.json +11 -0
- package/dist/esm/assets/locale/messages_sl.json +11 -0
- package/dist/esm/assets/locale/messages_sv.json +11 -0
- package/dist/esm/assets/locale/messages_th.json +11 -0
- package/dist/esm/assets/locale/messages_tl.json +11 -0
- package/dist/esm/assets/locale/messages_tr.json +11 -0
- package/dist/esm/assets/locale/messages_uk.json +11 -0
- package/dist/esm/assets/locale/messages_vi.json +11 -0
- package/dist/esm/assets/locale/messages_zh.json +11 -0
- package/dist/esm/assets/locale/version_file +1 -1
- package/dist/esm/assets/visual-e2e-locale/messages_visuale2e.json +29 -2
- package/dist/esm/hooks/index.js +0 -1
- package/dist/esm/hooks/index.js.map +1 -1
- package/dist/esm/hooks/useImport.js +2 -7
- package/dist/esm/hooks/useImport.js.map +1 -1
- package/dist/esm/index.js +0 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/state/CustomFieldModalState.js +8 -0
- package/dist/esm/state/CustomFieldModalState.js.map +1 -1
- package/dist/esm/state/ImportState/ImportState.js +299 -108
- package/dist/esm/state/ImportState/ImportState.js.map +1 -1
- package/dist/esm/state/ImportState/__tests__/ImportState.engine.spec.js +442 -55
- package/dist/esm/state/ImportState/__tests__/ImportState.engine.spec.js.map +1 -1
- package/dist/esm/state/ImportState/detectFieldType.js +19 -0
- package/dist/esm/state/ImportState/detectFieldType.js.map +1 -1
- package/dist/esm/state/ImportState/fieldEdit.js +44 -0
- package/dist/esm/state/ImportState/fieldEdit.js.map +1 -1
- package/dist/esm/state/TagsState/TagsManagementState.js +17 -33
- package/dist/esm/state/TagsState/TagsManagementState.js.map +1 -1
- package/dist/esm/state/TagsState/TagsSearchState.js +0 -6
- package/dist/esm/state/TagsState/TagsSearchState.js.map +1 -1
- package/dist/esm/testkit/createInMemorySchemaSource.js +140 -0
- package/dist/esm/testkit/createInMemorySchemaSource.js.map +1 -0
- package/dist/esm/testkit/hubJobImportMocks.js +17 -1
- package/dist/esm/testkit/hubJobImportMocks.js.map +1 -1
- package/dist/esm/testkit/index.js +1 -0
- package/dist/esm/testkit/index.js.map +1 -1
- package/dist/esm/types/SchemaSource.js.map +1 -1
- package/dist/types/hooks/index.d.ts +0 -1
- package/dist/types/hooks/index.d.ts.map +1 -1
- package/dist/types/hooks/useImport.d.ts +3 -18
- package/dist/types/hooks/useImport.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/services/translations.d.ts +20 -1
- package/dist/types/services/translations.d.ts.map +1 -1
- package/dist/types/state/CustomFieldModalState.d.ts +9 -0
- package/dist/types/state/CustomFieldModalState.d.ts.map +1 -1
- package/dist/types/state/ImportState/ImportState.d.ts +201 -62
- package/dist/types/state/ImportState/ImportState.d.ts.map +1 -1
- package/dist/types/state/ImportState/detectFieldType.d.ts +2 -0
- package/dist/types/state/ImportState/detectFieldType.d.ts.map +1 -1
- package/dist/types/state/ImportState/fieldEdit.d.ts +6 -0
- package/dist/types/state/ImportState/fieldEdit.d.ts.map +1 -1
- package/dist/types/state/TagsState/TagsManagementState.d.ts.map +1 -1
- package/dist/types/state/TagsState/TagsSearchState.d.ts.map +1 -1
- package/dist/types/testkit/WixPatternsContainer.driver.d.ts +4 -0
- package/dist/types/testkit/WixPatternsContainer.driver.d.ts.map +1 -1
- package/dist/types/testkit/createInMemorySchemaSource.d.ts +41 -0
- package/dist/types/testkit/createInMemorySchemaSource.d.ts.map +1 -0
- package/dist/types/testkit/hubJobImportMocks.d.ts +11 -1
- package/dist/types/testkit/hubJobImportMocks.d.ts.map +1 -1
- package/dist/types/testkit/index.d.ts +1 -0
- package/dist/types/testkit/index.d.ts.map +1 -1
- package/dist/types/types/FieldsSource.d.ts +5 -1
- package/dist/types/types/FieldsSource.d.ts.map +1 -1
- package/dist/types/types/Schema.d.ts +6 -0
- package/dist/types/types/Schema.d.ts.map +1 -1
- package/dist/types/types/SchemaSource.d.ts +6 -4
- package/dist/types/types/SchemaSource.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/cjs/hooks/useImportConfig.js +0 -10
- package/dist/cjs/hooks/useImportConfig.js.map +0 -1
- package/dist/esm/hooks/useImportConfig.js +0 -6
- package/dist/esm/hooks/useImportConfig.js.map +0 -1
- package/dist/types/hooks/useImportConfig.d.ts +0 -21
- package/dist/types/hooks/useImportConfig.d.ts.map +0 -1
|
@@ -7,16 +7,16 @@ import { rewriteParsedCsvForImport } from './rewriteCsv';
|
|
|
7
7
|
import { createHubJobClient } from './hubJobClient';
|
|
8
8
|
import { pollImportJob } from './importJobPoller';
|
|
9
9
|
import { detectColumnType, TYPE_SAMPLE_SIZE, } from './detectFieldType';
|
|
10
|
-
import { toFieldEdit } from './fieldEdit';
|
|
10
|
+
import { toDataExtensionFieldEdit, toFieldEdit } from './fieldEdit';
|
|
11
11
|
// `NewFieldType`, `detectColumnType`, etc. are re-exported from the package via
|
|
12
12
|
// the `ImportState` barrel (`./index`), which also exports `./detectFieldType`.
|
|
13
13
|
// Re-export the write-policy enum so consumers (and the cairo UI) reference it
|
|
14
14
|
// from `@wix/bex-core` rather than the bundled ambassador path.
|
|
15
15
|
export { WritePolicy } from '@wix/bex-utils/@wix/ambassador-hub-v1-job/types';
|
|
16
|
-
// How long to
|
|
17
|
-
// the
|
|
18
|
-
//
|
|
19
|
-
//
|
|
16
|
+
// How long to hold the completed (100%) progress bar on screen before moving to
|
|
17
|
+
// the summary step (CAIRO-4476). The job reports counts only when it finishes,
|
|
18
|
+
// so the bar sits at 0 for the whole import and would otherwise flip straight to
|
|
19
|
+
// the summary without the user ever seeing it reach completion.
|
|
20
20
|
const COMPLETION_LINGER_MS = 600;
|
|
21
21
|
// The Wix Data collection's id field key. A CSV column mapped to this field lets
|
|
22
22
|
// the user choose how rows that collide on `_id` are written (writePolicy).
|
|
@@ -90,6 +90,8 @@ export class ImportState {
|
|
|
90
90
|
this.result = {
|
|
91
91
|
succeeded: 0,
|
|
92
92
|
failed: 0,
|
|
93
|
+
created: 0,
|
|
94
|
+
updated: 0,
|
|
93
95
|
};
|
|
94
96
|
this.isBackingUp = false;
|
|
95
97
|
this.isRestoring = false;
|
|
@@ -99,22 +101,18 @@ export class ImportState {
|
|
|
99
101
|
// Id of the running data-movement job, set once submitted — used to cancel it
|
|
100
102
|
// (`terminateJob`) if the user closes mid-import.
|
|
101
103
|
this._jobId = null;
|
|
102
|
-
// The column whose pending new field the field editor is currently editing
|
|
103
|
-
//
|
|
104
|
-
// `
|
|
105
|
-
|
|
106
|
-
this.
|
|
107
|
-
this.
|
|
108
|
-
this.destination = props.destination;
|
|
109
|
-
this.backup = props.backup;
|
|
110
|
-
this.updateSchema = props.updateSchema;
|
|
104
|
+
// The column whose pending new field the field editor is currently editing, or
|
|
105
|
+
// `null` when the editor is closed. Observable so the source's field-editor
|
|
106
|
+
// component (rendered via `renderFieldEditor`) opens/prefills itself when this
|
|
107
|
+
// is set and closes when it clears — the editor is decoupled from the layout.
|
|
108
|
+
this.editingHeader = null;
|
|
109
|
+
this.config = props.config;
|
|
111
110
|
this.collection = props.collection;
|
|
112
111
|
this.importModalState = props.importModalState;
|
|
113
112
|
this.translate = props.translate;
|
|
114
113
|
this.errorHandler = props.errorHandler;
|
|
115
114
|
this.showToast = props.showToast;
|
|
116
115
|
this.downloadCsv = props.downloadCsv;
|
|
117
|
-
this.onJobError = props.onJobError;
|
|
118
116
|
this._detectColumnType = props.detectColumnType ?? detectColumnType;
|
|
119
117
|
this.hubJobClient = createHubJobClient({
|
|
120
118
|
httpClient: props.httpClient,
|
|
@@ -138,7 +136,12 @@ export class ImportState {
|
|
|
138
136
|
hasMappings: computed,
|
|
139
137
|
hasNoMappedData: computed,
|
|
140
138
|
selectedFieldIds: computed,
|
|
139
|
+
missingRequiredFields: computed,
|
|
140
|
+
hasMissingRequiredFields: computed,
|
|
141
141
|
idMappedHeader: computed,
|
|
142
|
+
editingHeader: observable.ref,
|
|
143
|
+
editingMapping: computed,
|
|
144
|
+
endFieldEdit: action.bound,
|
|
142
145
|
setWritePolicy: action.bound,
|
|
143
146
|
onUploadContinue: action.bound,
|
|
144
147
|
goToUpload: action.bound,
|
|
@@ -147,12 +150,56 @@ export class ImportState {
|
|
|
147
150
|
setNewFieldConfig: action.bound,
|
|
148
151
|
beginFieldEdit: action.bound,
|
|
149
152
|
applyFieldEdit: action.bound,
|
|
153
|
+
applyDataExtensionFieldEdit: action.bound,
|
|
150
154
|
close: action.bound,
|
|
155
|
+
done: action.bound,
|
|
156
|
+
downloadFailedRows: action.bound,
|
|
151
157
|
onFileSelected: action.bound,
|
|
152
158
|
startImport: action.bound,
|
|
153
159
|
reset: action.bound,
|
|
154
160
|
});
|
|
155
161
|
}
|
|
162
|
+
// Import inputs read live from `config` — a live schema-backed config exposes
|
|
163
|
+
// these as getters, a manual config as plain values; both read the same way.
|
|
164
|
+
/** Existing collection fields, for column auto-mapping. */
|
|
165
|
+
get fields() {
|
|
166
|
+
return this.config.fields;
|
|
167
|
+
}
|
|
168
|
+
/** Wix Data collection the import writes into (unset for platformized). */
|
|
169
|
+
get collectionId() {
|
|
170
|
+
const ds = this.config.dataSource;
|
|
171
|
+
return 'collectionId' in ds ? ds.collectionId : undefined;
|
|
172
|
+
}
|
|
173
|
+
get destination() {
|
|
174
|
+
const ds = this.config.dataSource;
|
|
175
|
+
return 'destination' in ds ? ds.destination : undefined;
|
|
176
|
+
}
|
|
177
|
+
get backup() {
|
|
178
|
+
return this.config.backup;
|
|
179
|
+
}
|
|
180
|
+
/** CMS create-fields callback, present only on the `collectionId` branch. */
|
|
181
|
+
get updateSchema() {
|
|
182
|
+
const ds = this.config.dataSource;
|
|
183
|
+
return 'collectionId' in ds ? ds.updateSchema : undefined;
|
|
184
|
+
}
|
|
185
|
+
/** DataExtension create-fields callback, present only on the `destination` branch. */
|
|
186
|
+
get createDataExtensionFields() {
|
|
187
|
+
const ds = this.config.dataSource;
|
|
188
|
+
return 'destination' in ds ? ds.createFields : undefined;
|
|
189
|
+
}
|
|
190
|
+
/** DataExtension field-management for the editor, on the `destination` branch. */
|
|
191
|
+
get dataExtensionFieldManagement() {
|
|
192
|
+
const ds = this.config.dataSource;
|
|
193
|
+
return 'destination' in ds ? ds.fieldManagement : undefined;
|
|
194
|
+
}
|
|
195
|
+
get jobApplicationErrorMap() {
|
|
196
|
+
return this.config.jobApplicationErrorMap;
|
|
197
|
+
}
|
|
198
|
+
// The collection's id-field key; a schema-backed config may report a non-`_id`
|
|
199
|
+
// identifier. Defaults to the Wix Data `_id`.
|
|
200
|
+
get idFieldKey() {
|
|
201
|
+
return this.config.idFieldKey ?? ID_FIELD_KEY;
|
|
202
|
+
}
|
|
156
203
|
init() {
|
|
157
204
|
return () => {
|
|
158
205
|
// Unmounting mid-import stops the poll loop (it checks `_cancelled`) so it
|
|
@@ -194,6 +241,30 @@ export class ImportState {
|
|
|
194
241
|
.filter((m) => m?.kind === 'existing')
|
|
195
242
|
.map((m) => m.fieldId));
|
|
196
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Required destination fields that no CSV column is mapped to (CAIRO-4452).
|
|
246
|
+
* A required field is an existing collection field, so only an `existing`
|
|
247
|
+
* mapping can satisfy it — a "create new field" column can't. Covers both
|
|
248
|
+
* ways a required field ends up unmapped: the CSV has no matching column at
|
|
249
|
+
* all, or the user set its column to "don't import". Drives the footer
|
|
250
|
+
* indicator and blocks `startImport`.
|
|
251
|
+
*/
|
|
252
|
+
get missingRequiredFields() {
|
|
253
|
+
return this.fields.filter((field) => field.required &&
|
|
254
|
+
field.id != null &&
|
|
255
|
+
!this.selectedFieldIds.has(field.id));
|
|
256
|
+
}
|
|
257
|
+
get hasMissingRequiredFields() {
|
|
258
|
+
return this.missingRequiredFields.length > 0;
|
|
259
|
+
}
|
|
260
|
+
// Comma-separated display names of the missing required fields, for the block
|
|
261
|
+
// toast. Falls back to the field id when a field carries no header.
|
|
262
|
+
_missingRequiredFieldNames() {
|
|
263
|
+
return this.missingRequiredFields
|
|
264
|
+
.map((field) => field.header || field.id || '')
|
|
265
|
+
.filter(Boolean)
|
|
266
|
+
.join(', ');
|
|
267
|
+
}
|
|
197
268
|
/**
|
|
198
269
|
* The CSV header currently mapped to the collection's `_id` field, or `null`
|
|
199
270
|
* when no column targets it. Drives both the "When items share the same ID"
|
|
@@ -201,7 +272,8 @@ export class ImportState {
|
|
|
201
272
|
* with no `_id` mapped, every row is a fresh insert and collisions can't occur.
|
|
202
273
|
*/
|
|
203
274
|
get idMappedHeader() {
|
|
204
|
-
const
|
|
275
|
+
const idFieldKey = this.idFieldKey;
|
|
276
|
+
const entry = Object.entries(this.mappingByHeader).find(([, mapping]) => mapping?.kind === 'existing' && mapping.fieldId === idFieldKey);
|
|
205
277
|
return entry ? entry[0] : null;
|
|
206
278
|
}
|
|
207
279
|
setWritePolicy(policy) {
|
|
@@ -209,12 +281,13 @@ export class ImportState {
|
|
|
209
281
|
}
|
|
210
282
|
/**
|
|
211
283
|
* Whether columns may be mapped to a brand-new field created during import.
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
284
|
+
* The opt-in is the presence of the active `dataSource` branch's create
|
|
285
|
+
* callback — `updateSchema` (CMS) or `createFields` (DataExtension). A source
|
|
286
|
+
* that can't create fields (e.g. no create permission) supplies neither, so
|
|
287
|
+
* columns fall back to "don't import" (see `getDefaultMapping`).
|
|
215
288
|
*/
|
|
216
289
|
get canCreateNewField() {
|
|
217
|
-
return this.updateSchema != null;
|
|
290
|
+
return (this.updateSchema != null || this.createDataExtensionFields != null);
|
|
218
291
|
}
|
|
219
292
|
/**
|
|
220
293
|
* Whether the write-policy (id-collision) choice is meaningful for this import.
|
|
@@ -246,7 +319,10 @@ export class ImportState {
|
|
|
246
319
|
this.result = {
|
|
247
320
|
succeeded: 0,
|
|
248
321
|
failed: 0,
|
|
322
|
+
created: 0,
|
|
323
|
+
updated: 0,
|
|
249
324
|
};
|
|
325
|
+
this._pendingFieldsMap = undefined;
|
|
250
326
|
this.isBackingUp = false;
|
|
251
327
|
this.isRestoring = false;
|
|
252
328
|
this.restoreFn = null;
|
|
@@ -255,11 +331,10 @@ export class ImportState {
|
|
|
255
331
|
}
|
|
256
332
|
async close() {
|
|
257
333
|
// While on the progress step we treat close as a cancel — terminate the job
|
|
258
|
-
// and restore the backup — UNLESS the import already succeeded. On
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
// lets the success path do its collection refresh.
|
|
334
|
+
// and restore the backup — UNLESS the import already succeeded. On success
|
|
335
|
+
// `startImport` moves straight to the 'summary' step, so a close from there
|
|
336
|
+
// just closes the modal (the summary's own `done` is the normal exit); this
|
|
337
|
+
// guard covers only closing mid-import.
|
|
263
338
|
if (this.currentStep === 'progress' && !this.importStatus.isSuccess) {
|
|
264
339
|
this._cancelled = true;
|
|
265
340
|
this._abortController?.abort();
|
|
@@ -282,6 +357,13 @@ export class ImportState {
|
|
|
282
357
|
}
|
|
283
358
|
this.importModalState.close();
|
|
284
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* Closes the summary step at the end of a completed import. The collection was
|
|
362
|
+
* already refreshed when the summary opened, so this only dismisses the modal.
|
|
363
|
+
*/
|
|
364
|
+
done() {
|
|
365
|
+
this.importModalState.close();
|
|
366
|
+
}
|
|
285
367
|
async _restoreBackup() {
|
|
286
368
|
const restoreFn = this.restoreFn;
|
|
287
369
|
if (!restoreFn) {
|
|
@@ -450,50 +532,44 @@ export class ImportState {
|
|
|
450
532
|
},
|
|
451
533
|
});
|
|
452
534
|
}
|
|
453
|
-
_showPartialSuccessToast() {
|
|
454
|
-
this.showToast?.({
|
|
455
|
-
type: 'WARNING',
|
|
456
|
-
message: this.translate('cairo.import.partialSuccessToast', {
|
|
457
|
-
failed: String(this.result.failed),
|
|
458
|
-
imported: String(this.result.succeeded),
|
|
459
|
-
}),
|
|
460
|
-
action: {
|
|
461
|
-
text: this.translate('cairo.import.downloadFailedRows'),
|
|
462
|
-
onClick: () => this._downloadFailedRows(),
|
|
463
|
-
removeToastOnClick: true,
|
|
464
|
-
},
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
535
|
/**
|
|
468
|
-
* Builds a
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
536
|
+
* Builds a re-importable CSV of the rows the server rejected and hands it to
|
|
537
|
+
* `downloadCsv`. Each failed row is prefixed with a leading "why it failed"
|
|
538
|
+
* column holding the server's error message, so the user can see and fix each
|
|
539
|
+
* reason. The failed rows (and their reasons) come from the job's movement
|
|
540
|
+
* logs, mapped back to the parsed `csvRows` we still hold in memory.
|
|
472
541
|
*/
|
|
473
|
-
async
|
|
542
|
+
async downloadFailedRows() {
|
|
474
543
|
const jobId = this._jobId;
|
|
475
544
|
if (!jobId) {
|
|
476
545
|
return;
|
|
477
546
|
}
|
|
478
|
-
let
|
|
547
|
+
let failures;
|
|
479
548
|
try {
|
|
480
|
-
|
|
549
|
+
failures = await this._fetchFailedRows(jobId);
|
|
481
550
|
}
|
|
482
551
|
catch (error) {
|
|
483
552
|
console.error('Fetching failed rows report failed', error);
|
|
484
553
|
return;
|
|
485
554
|
}
|
|
486
|
-
const
|
|
487
|
-
.map((index) =>
|
|
555
|
+
const rows = failures
|
|
556
|
+
.map(({ index, reason }) => {
|
|
557
|
+
const row = this.csvRows[index];
|
|
558
|
+
return row != null ? [reason, ...row] : null;
|
|
559
|
+
})
|
|
488
560
|
.filter((row) => row != null);
|
|
489
|
-
if (
|
|
561
|
+
if (rows.length === 0) {
|
|
490
562
|
return;
|
|
491
563
|
}
|
|
492
|
-
const
|
|
564
|
+
const headers = [
|
|
565
|
+
this.translate('cairo.import.failureReasonColumn'),
|
|
566
|
+
...this.csvHeaders,
|
|
567
|
+
];
|
|
568
|
+
const content = unparseCsv(headers, rows);
|
|
493
569
|
this.downloadCsv?.(content, `failed-${this.fileName || 'import.csv'}`);
|
|
494
570
|
}
|
|
495
|
-
// Pages through the job's movement logs and returns
|
|
496
|
-
// the
|
|
571
|
+
// Pages through the job's movement logs and returns, for each row that failed,
|
|
572
|
+
// its `csvRows` index and the server's failure reason. data-hub's CSV decoder
|
|
497
573
|
// (wix-data-hub: packages/wix-data-hub-executor-c/src/plugins/formats/csv/
|
|
498
574
|
// csv-binary-decoder.ts) parses with the `csv-parse` library (`columns: true`)
|
|
499
575
|
// and sets `sourceItemId = "row ${++index + 1}"`, incrementing once **per
|
|
@@ -502,8 +578,8 @@ export class ImportState {
|
|
|
502
578
|
// `csvRows[0]`, i.e. `csvRows[N - 2]`. Because `csv-parse` is RFC-4180,
|
|
503
579
|
// quoted cells containing embedded newlines are a single record, so they do
|
|
504
580
|
// NOT shift the numbering. Out-of-range indices are guarded below.
|
|
505
|
-
async
|
|
506
|
-
const
|
|
581
|
+
async _fetchFailedRows(jobId) {
|
|
582
|
+
const failures = [];
|
|
507
583
|
let cursor;
|
|
508
584
|
do {
|
|
509
585
|
const { logs, pagingMetadata } = await this.hubJobClient.listMovementLogs(jobId, cursor ? { cursor } : undefined);
|
|
@@ -516,13 +592,13 @@ export class ImportState {
|
|
|
516
592
|
if (Number.isInteger(index) &&
|
|
517
593
|
index >= 0 &&
|
|
518
594
|
index < this.csvRows.length) {
|
|
519
|
-
|
|
595
|
+
failures.push({ index, reason: log.failure.description ?? '' });
|
|
520
596
|
}
|
|
521
597
|
}
|
|
522
598
|
cursor =
|
|
523
599
|
(pagingMetadata?.hasNext && pagingMetadata.cursors?.next) || undefined;
|
|
524
600
|
} while (cursor);
|
|
525
|
-
return
|
|
601
|
+
return failures;
|
|
526
602
|
}
|
|
527
603
|
setMapping(header, fieldId) {
|
|
528
604
|
this.mappingByHeader = {
|
|
@@ -560,23 +636,51 @@ export class ImportState {
|
|
|
560
636
|
};
|
|
561
637
|
}
|
|
562
638
|
/**
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
*
|
|
639
|
+
* Opens the field editor for a column's pending new field: sets
|
|
640
|
+
* `editingHeader`, which the source's editor component watches to open and
|
|
641
|
+
* prefill itself. No-op if the column isn't mapped to a new field.
|
|
566
642
|
*/
|
|
567
643
|
beginFieldEdit(header) {
|
|
568
|
-
this.
|
|
644
|
+
if (this.mappingByHeader[header]?.kind === 'new') {
|
|
645
|
+
this.editingHeader = header;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/** Closes the field editor (clears `editingHeader`). */
|
|
649
|
+
endFieldEdit() {
|
|
650
|
+
this.editingHeader = null;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* The `new` mapping currently being edited, or `null` when the editor is
|
|
654
|
+
* closed. The editor component reads this to prefill its form.
|
|
655
|
+
*/
|
|
656
|
+
get editingMapping() {
|
|
657
|
+
if (this.editingHeader == null) {
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
const mapping = this.mappingByHeader[this.editingHeader];
|
|
661
|
+
return mapping?.kind === 'new' ? mapping : null;
|
|
569
662
|
}
|
|
570
663
|
/**
|
|
571
|
-
* `onSubmit` handler for the field editor: maps the edited field spec into
|
|
572
|
-
* column being edited (set via `beginFieldEdit`). No-op if nothing is being
|
|
664
|
+
* `onSubmit` handler for the CMS field editor: maps the edited field spec into
|
|
665
|
+
* the column being edited (set via `beginFieldEdit`). No-op if nothing is being
|
|
573
666
|
* edited.
|
|
574
667
|
*/
|
|
575
668
|
applyFieldEdit(field) {
|
|
576
|
-
if (this.
|
|
669
|
+
if (this.editingHeader == null) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
this.setNewFieldConfig(this.editingHeader, toFieldEdit(field));
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* `onSubmit` handler for the DataExtension field editor: maps the edited
|
|
676
|
+
* DataExtension draft (which carries per-audience permissions) into the column
|
|
677
|
+
* being edited. The `destination`-branch counterpart to `applyFieldEdit`.
|
|
678
|
+
*/
|
|
679
|
+
applyDataExtensionFieldEdit(draft) {
|
|
680
|
+
if (this.editingHeader == null) {
|
|
577
681
|
return;
|
|
578
682
|
}
|
|
579
|
-
this.setNewFieldConfig(this.
|
|
683
|
+
this.setNewFieldConfig(this.editingHeader, toDataExtensionFieldEdit(draft));
|
|
580
684
|
}
|
|
581
685
|
/**
|
|
582
686
|
* Runs the server-side import: align the CSV to the resolved field keys,
|
|
@@ -621,21 +725,30 @@ export class ImportState {
|
|
|
621
725
|
// The job ran server-side, so a failure arrives on a 200 (the poll
|
|
622
726
|
// succeeded; the job didn't) with no HTTP error to carry the application
|
|
623
727
|
// error. Build a synthetic, HTTP-error-shaped error (the error handler
|
|
624
|
-
// reads the code from `response.data.details.applicationError`)
|
|
625
|
-
//
|
|
626
|
-
//
|
|
627
|
-
//
|
|
628
|
-
//
|
|
629
|
-
//
|
|
630
|
-
//
|
|
631
|
-
// Without a
|
|
632
|
-
// generic message.
|
|
728
|
+
// reads the code from `response.data.details.applicationError`), then
|
|
729
|
+
// resolve it through the consumer's per-code map via `withErrorHandler` —
|
|
730
|
+
// the same way as `fetchData` etc. `job.error` is the consumer's
|
|
731
|
+
// application error (their import business logic), so only they can map
|
|
732
|
+
// its codes; they supply just the code→message map and we do the
|
|
733
|
+
// wrapping. `withErrorHandler` attaches the map to the error and
|
|
734
|
+
// re-rejects it, so the progress step resolves a consumer-specific
|
|
735
|
+
// message via `getResolvedError`. Without a map the same error still
|
|
736
|
+
// throws, falling back to the generic message.
|
|
633
737
|
const error = Object.assign(new Error(`ImportJob.${job.error?.code ?? 'failed'}`), { response: { data: { details: { applicationError: job.error } } } });
|
|
634
|
-
const
|
|
635
|
-
if (
|
|
636
|
-
//
|
|
637
|
-
// `
|
|
638
|
-
|
|
738
|
+
const jobApplicationErrorMap = this.jobApplicationErrorMap;
|
|
739
|
+
if (jobApplicationErrorMap) {
|
|
740
|
+
// Adapt the consumer's code→message map into the error handler's
|
|
741
|
+
// `errorCodesMap`: each entry, when its code matches the job's
|
|
742
|
+
// application error, resolves with that error. `withErrorHandler`
|
|
743
|
+
// attaches the resolved message to `error` and re-rejects it, so the
|
|
744
|
+
// progress step surfaces it via `getResolvedError` — the consumer only
|
|
745
|
+
// supplies the map, not the wrapping.
|
|
746
|
+
const appError = (job.error ?? {});
|
|
747
|
+
const errorCodesMap = Object.fromEntries(Object.entries(jobApplicationErrorMap).map(([code, resolve]) => [
|
|
748
|
+
code,
|
|
749
|
+
() => resolve(appError),
|
|
750
|
+
]));
|
|
751
|
+
await this.errorHandler.withErrorHandler(() => Promise.reject(error), errorCodesMap);
|
|
639
752
|
}
|
|
640
753
|
throw error;
|
|
641
754
|
}
|
|
@@ -664,13 +777,21 @@ export class ImportState {
|
|
|
664
777
|
const succeeded = progress?.itemsSuccessful ?? 0;
|
|
665
778
|
const failed = progress?.itemsRejected ?? 0;
|
|
666
779
|
const current = progress?.current ?? succeeded + failed;
|
|
780
|
+
// The server splits successful items into created (new) and updated
|
|
781
|
+
// (existing) — the summary step reports each. Absent on older jobs, in
|
|
782
|
+
// which case both stay 0 and only the total `succeeded` is shown.
|
|
783
|
+
const created = progress?.itemsSuccessfulInfo?.itemsCreated ?? 0;
|
|
784
|
+
const updated = progress?.itemsSuccessfulInfo?.itemsUpdated ?? 0;
|
|
667
785
|
// `totalCount` is fixed up front in `startImport` from the CSV row count
|
|
668
786
|
// (one uploaded row per CSV row); polling only advances `processedCount`.
|
|
669
787
|
runInAction(() => {
|
|
670
788
|
this.processedCount = Math.min(current, this.totalCount);
|
|
671
|
-
this.result = { succeeded, failed };
|
|
789
|
+
this.result = { succeeded, failed, created, updated };
|
|
672
790
|
});
|
|
673
791
|
}
|
|
792
|
+
_lingerOnCompletion() {
|
|
793
|
+
return new Promise((resolve) => setTimeout(resolve, COMPLETION_LINGER_MS));
|
|
794
|
+
}
|
|
674
795
|
async _runBackup() {
|
|
675
796
|
const backup = this.backup;
|
|
676
797
|
if (!backup) {
|
|
@@ -703,9 +824,6 @@ export class ImportState {
|
|
|
703
824
|
});
|
|
704
825
|
return false;
|
|
705
826
|
}
|
|
706
|
-
_lingerOnCompletion() {
|
|
707
|
-
return new Promise((resolve) => setTimeout(resolve, COMPLETION_LINGER_MS));
|
|
708
|
-
}
|
|
709
827
|
/**
|
|
710
828
|
* Resolves every column to the final destination key used in the upload
|
|
711
829
|
* payload: an existing field id, a new field's (Cairo-decided) key, or `null`
|
|
@@ -715,13 +833,16 @@ export class ImportState {
|
|
|
715
833
|
*/
|
|
716
834
|
async _resolveFieldMapping() {
|
|
717
835
|
const resolved = {};
|
|
718
|
-
|
|
836
|
+
// Pending new columns with their deduped key + display name. Source-neutral;
|
|
837
|
+
// the per-source field def is built from these in `_createPendingFields`.
|
|
838
|
+
const pending = [];
|
|
719
839
|
// Seed with existing field ids/display names so derived keys and names never
|
|
720
840
|
// collide with them or with each other.
|
|
721
841
|
const usedKeys = new Set(this.fields.map((field) => field.id).filter((id) => !!id));
|
|
722
842
|
const usedNames = new Set(this.fields
|
|
723
843
|
.map((field) => field.header)
|
|
724
844
|
.filter((header) => !!header));
|
|
845
|
+
const canCreate = this.canCreateNewField;
|
|
725
846
|
for (const header of this.csvHeaders) {
|
|
726
847
|
const mapping = this.mappingByHeader[header];
|
|
727
848
|
if (mapping == null) {
|
|
@@ -730,35 +851,75 @@ export class ImportState {
|
|
|
730
851
|
else if (mapping.kind === 'existing') {
|
|
731
852
|
resolved[header] = mapping.fieldId;
|
|
732
853
|
}
|
|
733
|
-
else if (
|
|
854
|
+
else if (canCreate) {
|
|
734
855
|
// The user's name override (from the field editor) seeds the unique
|
|
735
856
|
// name/key derivation; otherwise fall back to the column header.
|
|
736
857
|
const baseName = mapping.displayName?.trim() || header;
|
|
737
858
|
const { displayName, key } = uniqueFieldNameAndKey(baseName, usedNames, usedKeys);
|
|
738
859
|
usedKeys.add(key);
|
|
739
860
|
usedNames.add(displayName);
|
|
740
|
-
|
|
741
|
-
key,
|
|
742
|
-
type: mapping.fieldType,
|
|
743
|
-
displayName,
|
|
744
|
-
...(mapping.config && { config: mapping.config }),
|
|
745
|
-
});
|
|
861
|
+
pending.push({ key, displayName, mapping });
|
|
746
862
|
resolved[header] = key;
|
|
747
863
|
}
|
|
748
864
|
else {
|
|
749
|
-
// "new" mapping
|
|
865
|
+
// "new" mapping with no create callback on this source — nothing to map into.
|
|
750
866
|
resolved[header] = null;
|
|
751
867
|
}
|
|
752
868
|
}
|
|
753
|
-
if (
|
|
754
|
-
|
|
869
|
+
if (pending.length > 0) {
|
|
870
|
+
// Stash the returned fields map to merge into the table on success; kept off
|
|
871
|
+
// the observable until then so columns don't update mid-import.
|
|
872
|
+
this._pendingFieldsMap = await this._createPendingFields(pending);
|
|
755
873
|
}
|
|
756
874
|
return resolved;
|
|
757
875
|
}
|
|
876
|
+
/**
|
|
877
|
+
* Creates the pending new fields via the active `dataSource` branch's callback
|
|
878
|
+
* — `updateSchema` (CMS) or `createFields` (DataExtension) — building the
|
|
879
|
+
* source-specific field def from the shared, already-deduped pending columns.
|
|
880
|
+
* The callback may throw; `startImport` surfaces that as the error state.
|
|
881
|
+
*/
|
|
882
|
+
async _createPendingFields(pending) {
|
|
883
|
+
const updateSchema = this.updateSchema;
|
|
884
|
+
const createDataExtensionFields = this.createDataExtensionFields;
|
|
885
|
+
let updated;
|
|
886
|
+
if (updateSchema) {
|
|
887
|
+
updated = await updateSchema(pending.map(({ key, displayName, mapping }) => ({
|
|
888
|
+
key,
|
|
889
|
+
type: mapping.fieldType,
|
|
890
|
+
displayName,
|
|
891
|
+
...(mapping.config && { config: mapping.config }),
|
|
892
|
+
})));
|
|
893
|
+
}
|
|
894
|
+
else if (createDataExtensionFields) {
|
|
895
|
+
// The DataExtension source builds its own field shape from the neutral
|
|
896
|
+
// pending columns — no DataExtension-specific def-building in core.
|
|
897
|
+
updated = await createDataExtensionFields(pending);
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
return undefined;
|
|
901
|
+
}
|
|
902
|
+
// Guard the empty map: SchemaState.mergeFields is a full replace, so merging
|
|
903
|
+
// `{}` would wipe every column — treat a missing/empty return as "nothing to
|
|
904
|
+
// merge".
|
|
905
|
+
return updated && Object.keys(updated).length > 0 ? updated : undefined;
|
|
906
|
+
}
|
|
758
907
|
async startImport() {
|
|
759
908
|
if (!this.hasMappings) {
|
|
760
909
|
return;
|
|
761
910
|
}
|
|
911
|
+
// Block import while any required destination field is unmapped and tell the
|
|
912
|
+
// user which (CAIRO-4452). The footer already flags this; the Import button
|
|
913
|
+
// stays enabled, so this is where the click is stopped.
|
|
914
|
+
if (this.hasMissingRequiredFields) {
|
|
915
|
+
this.showToast?.({
|
|
916
|
+
type: 'ERROR',
|
|
917
|
+
message: this.translate('cairo.import.missingRequiredMessage', {
|
|
918
|
+
fields: this._missingRequiredFieldNames(),
|
|
919
|
+
}),
|
|
920
|
+
});
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
762
923
|
this._cancelled = false;
|
|
763
924
|
this._abortController = new AbortController();
|
|
764
925
|
// One uploaded row per CSV row (the rewrite drops columns, not rows), so the
|
|
@@ -775,8 +936,13 @@ export class ImportState {
|
|
|
775
936
|
this.result = {
|
|
776
937
|
succeeded: 0,
|
|
777
938
|
failed: 0,
|
|
939
|
+
created: 0,
|
|
940
|
+
updated: 0,
|
|
778
941
|
};
|
|
779
942
|
this._jobId = null;
|
|
943
|
+
// Drop any fields map from a prior attempt so a no-new-fields "Try again"
|
|
944
|
+
// can't merge stale fields.
|
|
945
|
+
this._pendingFieldsMap = undefined;
|
|
780
946
|
});
|
|
781
947
|
const backupFailed = await this._runBackup();
|
|
782
948
|
if (backupFailed) {
|
|
@@ -809,6 +975,11 @@ export class ImportState {
|
|
|
809
975
|
...queryStatus.loading,
|
|
810
976
|
promise,
|
|
811
977
|
};
|
|
978
|
+
// Assigned only on the success path below, so the post-import merge (after the
|
|
979
|
+
// try/catch) fires only for a completed import — without re-checking status
|
|
980
|
+
// there. Reading `_pendingFieldsMap` directly wouldn't do: it's set pre-upload,
|
|
981
|
+
// so it stays truthy even when the job later fails.
|
|
982
|
+
let createdFields;
|
|
812
983
|
try {
|
|
813
984
|
await promise;
|
|
814
985
|
if (this._cancelled) {
|
|
@@ -819,24 +990,30 @@ export class ImportState {
|
|
|
819
990
|
...queryStatus.success,
|
|
820
991
|
data: this.result,
|
|
821
992
|
};
|
|
993
|
+
// The job reports counts only when it finishes, so the bar sat at 0 for
|
|
994
|
+
// the whole import (CAIRO-4476). Snap it to 100% and hold it (below)
|
|
995
|
+
// before the summary step replaces the progress step, so the user sees
|
|
996
|
+
// the import reach completion.
|
|
997
|
+
this.processedCount = this.totalCount;
|
|
822
998
|
});
|
|
823
|
-
//
|
|
824
|
-
//
|
|
825
|
-
// the
|
|
826
|
-
|
|
827
|
-
if (this.
|
|
828
|
-
|
|
829
|
-
if (this._cancelled) {
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
999
|
+
// Keep the completed bar on screen briefly before moving on. `importStatus`
|
|
1000
|
+
// is already success, so a close during this window just dismisses the
|
|
1001
|
+
// modal (the `close()` guard doesn't treat it as a cancel).
|
|
1002
|
+
await this._lingerOnCompletion();
|
|
1003
|
+
if (this._cancelled) {
|
|
1004
|
+
return;
|
|
832
1005
|
}
|
|
1006
|
+
// Success path only. `_pendingFieldsMap` holds this run's returned map — it's
|
|
1007
|
+
// cleared at the start of `startImport` (the stale-guard) and set by
|
|
1008
|
+
// `_resolveFieldMapping`; snapshotting here keeps the merge success-gated.
|
|
1009
|
+
createdFields = this._pendingFieldsMap;
|
|
833
1010
|
runInAction(() => {
|
|
834
|
-
//
|
|
835
|
-
//
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
this.
|
|
1011
|
+
// Import finished: show the summary step (created / updated / failed
|
|
1012
|
+
// counts, plus a "Download CSV" of the failed rows when any failed).
|
|
1013
|
+
// The modal stays open; the user dismisses it from the summary via
|
|
1014
|
+
// `done`. Refresh the collection now so the table behind the modal is
|
|
1015
|
+
// up to date by the time it closes.
|
|
1016
|
+
this.currentStep = 'summary';
|
|
840
1017
|
this.collection.clearResultAndMoveToStart({ force: true });
|
|
841
1018
|
});
|
|
842
1019
|
}
|
|
@@ -848,6 +1025,20 @@ export class ImportState {
|
|
|
848
1025
|
};
|
|
849
1026
|
});
|
|
850
1027
|
}
|
|
1028
|
+
// Merge the created fields into the table so the new columns appear with no
|
|
1029
|
+
// manual reload (CAIRO-4400). Called imperatively here, outside the try/catch
|
|
1030
|
+
// above, so a merge failure can't flip a completed import into the error state
|
|
1031
|
+
// (whose "Try again" re-imports every row). `createdFields` is set only on the
|
|
1032
|
+
// success path and only when fields were created.
|
|
1033
|
+
if (createdFields) {
|
|
1034
|
+
try {
|
|
1035
|
+
this.config.mergeFields?.(createdFields);
|
|
1036
|
+
}
|
|
1037
|
+
catch (error) {
|
|
1038
|
+
// eslint-disable-next-line no-console
|
|
1039
|
+
console.error('ImportState config.mergeFields failed', error);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
851
1042
|
}
|
|
852
1043
|
}
|
|
853
1044
|
//# sourceMappingURL=ImportState.js.map
|