ngx-dynamic-entity 1.12.0 → 1.14.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.
@@ -1,9 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Type, EnvironmentProviders, WritableSignal, OnInit, OnChanges, OnDestroy, EventEmitter, SimpleChanges } from '@angular/core';
3
- import { ReferenceOption as ReferenceOption$1, LocalizedText, EntityReferenceLoader as EntityReferenceLoader$1, LookupListSource as LookupListSource$1, RecordMigration as RecordMigration$1, FieldValidators, NestedFieldConfig, ReferenceLoaderContext, EntityFormConfig, DropdownOption, LookupListValue, NestedTabConfig, FormRule, RuleEvaluationResult as RuleEvaluationResult$1 } from '@dynamic-entity/core';
2
+ import { InjectionToken, Type, EnvironmentProviders, WritableSignal, OnInit, OnChanges, OnDestroy, EventEmitter, SimpleChanges, Provider } from '@angular/core';
3
+ import { ReferenceOption, LocalizedText, EntityFormConfig, FormRule, ImportLookups, MappingPlan, ImportResult, TemplateSpec, SheetData, CommonModuleEntry, EntityReferenceLoader, LookupListSource, RecordMigration, FileUploadHandler, FieldValidators, NestedFieldConfig, ReferenceLoaderContext, DropdownOption, LookupListValue, NestedTabConfig, RuleEvaluationResult, ImportColumn, UnsupportedColumn, ImportRowError } from '@dynamic-entity/core';
4
4
  export { AutoPatchConfig, CommonModuleEntry, ComponentClass, DropdownOption, EntityFormConfig, EntityPermissions, EntityReferenceConfig, EntityReferenceLoader, FieldTableConfig, FieldValidators, FileRef, FormRule, LocalizedText, NestedFieldConfig, NestedTabConfig, PatchOnTrueMapping, RbacContext, ReferenceLoaderContext, ReferenceOption, RichFieldType, RuleAction, RuleCondition, RuleEvaluationResult, VersionedRecord, formatDisplayValue, normalizeConfig, normalizeOption, resolveLabel, resolveOptionLabel, resolveOptionValue, valuesMatch } from '@dynamic-entity/core';
5
5
  import { ValidatorFn, AsyncValidatorFn, ValidationErrors, FormGroup, FormArray, AbstractControl } from '@angular/forms';
6
6
  import * as rxjs from 'rxjs';
7
+ import * as ngx_dynamic_entity from 'ngx-dynamic-entity';
7
8
 
8
9
  /**
9
10
  * Backing store for cached reference options.
@@ -15,8 +16,8 @@ import * as rxjs from 'rxjs';
15
16
  * that must not silently persist a tenant's data.
16
17
  */
17
18
  interface EntityRefCacheStore {
18
- get(key: string): ReferenceOption$1[] | undefined;
19
- set(key: string, value: ReferenceOption$1[]): void;
19
+ get(key: string): ReferenceOption[] | undefined;
20
+ set(key: string, value: ReferenceOption[]): void;
20
21
  delete(key: string): void;
21
22
  /** Every key currently held — used for prefix invalidation of one entity. */
22
23
  keys(): string[];
@@ -25,8 +26,8 @@ interface EntityRefCacheStore {
25
26
  /** Default in-memory store. Lives as long as the injector that created it. */
26
27
  declare class InMemoryEntityRefCacheStore implements EntityRefCacheStore {
27
28
  private readonly map;
28
- get(key: string): ReferenceOption$1[] | undefined;
29
- set(key: string, value: ReferenceOption$1[]): void;
29
+ get(key: string): ReferenceOption[] | undefined;
30
+ set(key: string, value: ReferenceOption[]): void;
30
31
  delete(key: string): void;
31
32
  keys(): string[];
32
33
  clear(): void;
@@ -95,6 +96,40 @@ declare const DEFAULT_UI_TEXT: {
95
96
  criticalFieldUnlockHint: string;
96
97
  dismissBanner: string;
97
98
  jumpToField: string;
99
+ importUpload: string;
100
+ importMap: string;
101
+ importReview: string;
102
+ importDone: string;
103
+ importChooseFile: string;
104
+ importFileChosen: string;
105
+ importReading: string;
106
+ importNeedFile: string;
107
+ importTemplateHeading: string;
108
+ importTemplateExplain: string;
109
+ importTemplateDownload: string;
110
+ importTemplateSelectAll: string;
111
+ importTemplateSelectNone: string;
112
+ importTemplateUnsupported: string;
113
+ importColumnInFile: string;
114
+ importTargetField: string;
115
+ importUnmapped: string;
116
+ importGuessed: string;
117
+ importMappedCount: string;
118
+ importRequiredUnmapped: string;
119
+ importPreviewHeading: string;
120
+ importRowRejected: string;
121
+ importRunImport: string;
122
+ importImporting: string;
123
+ importSucceeded: string;
124
+ importSkippedRows: string;
125
+ importFailedRows: string;
126
+ importErrorsTruncated: string;
127
+ importStartOver: string;
128
+ importErrorsHeading: string;
129
+ importErrorRow: string;
130
+ importErrorField: string;
131
+ importErrorReason: string;
132
+ importPlanProblem: string;
98
133
  entityLabel: string;
99
134
  criticalFieldChanged: string;
100
135
  recordModified: string;
@@ -162,6 +197,102 @@ declare class UiTextService {
162
197
  static ɵprov: i0.ɵɵInjectableDeclaration<UiTextService>;
163
198
  }
164
199
 
200
+ /**
201
+ * import-contracts.ts — the two seams the import wizard hangs on.
202
+ *
203
+ * Both follow the pattern the rest of this package already uses for anything the library
204
+ * cannot decide for a consumer — `FileUploadHandler`, `EntityReferenceLoader`,
205
+ * `LookupListLoader`: a function type here, a token to register it against, and a default
206
+ * that works without registering anything.
207
+ *
208
+ * **Why two rather than one.** Reading a file and deciding where the work happens are
209
+ * different questions with different answers. A consumer may want `.xlsx` support in a
210
+ * wizard that still runs entirely in the browser, or plain CSV that is nonetheless processed
211
+ * on a server because their files have fifty thousand rows. Collapsing them into one option
212
+ * would make each answer imply the other.
213
+ */
214
+
215
+ /**
216
+ * Reads an uploaded file into headers and positional rows.
217
+ *
218
+ * Positional (`string[][]`), not keyed by header, because a real sheet carries duplicate
219
+ * headers and blank ones — and because a mapping entry addresses a column by index for
220
+ * exactly that reason. A parser that returned objects keyed by header would silently lose
221
+ * the second of two columns both called "Notes".
222
+ *
223
+ * The library ships a CSV implementation and no more. Registering one that understands
224
+ * `.xlsx` is how a consumer adds it, and which spreadsheet library they use stays their
225
+ * choice — this package has no runtime dependency and gains none here.
226
+ *
227
+ * @example
228
+ * provideNgxDynamicEntity({
229
+ * sheetParser: async file => {
230
+ * const workbook = XLSX.read(await file.arrayBuffer());
231
+ * const rows = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]], {
232
+ * header: 1, raw: false, defval: '',
233
+ * }) as string[][];
234
+ * return { headers: rows[0] ?? [], rows: rows.slice(1) };
235
+ * },
236
+ * })
237
+ */
238
+ type SheetParser = (file: File) => SheetData | Promise<SheetData>;
239
+ /** The formats a generated template can be written in. CSV needs no dependency. */
240
+ type TemplateFormat = 'csv' | 'xlsx';
241
+ /** What the wizard needs to know about the entity it is importing into. */
242
+ interface ImportContext {
243
+ config: EntityFormConfig;
244
+ /**
245
+ * Rules that apply to this entity.
246
+ *
247
+ * Without them an import checks field validators only, which is **not** what the form
248
+ * enforces: a rule can attach an error, and a rule can hide a field, which must relax
249
+ * `required`. Pass the same rules the form is rendered with or the two disagree.
250
+ */
251
+ rules?: readonly FormRule[];
252
+ /** Values for any `listName` field, already resolved — core cannot reach `LOOKUP_REGISTRY`. */
253
+ lookups?: ImportLookups;
254
+ lang?: string;
255
+ }
256
+ /** What a first look at an uploaded file yields. */
257
+ interface ImportPreview {
258
+ headers: string[];
259
+ /** The first few rows, for the mapping screen to show under each column. */
260
+ sample: string[][];
261
+ /** Where the suggester got to. Every inferred match is tagged `guess`. */
262
+ suggestion: MappingPlan;
263
+ /** Total data rows in the file, which may be far more than `sample` holds. */
264
+ rowCount: number;
265
+ }
266
+ /**
267
+ * Where the work happens.
268
+ *
269
+ * The default implementation does everything in the browser with `@dynamic-entity/core` and
270
+ * never touches the network, so the wizard is fully usable with no backend at all. Register
271
+ * an implementation that posts to a server and the same components drive it instead — the
272
+ * engine underneath is the same code either way, which is the point of it being pure.
273
+ *
274
+ * This is the real answer to "should parsing happen on the client or the server?": both,
275
+ * chosen at registration, without a second implementation of the mapping rules.
276
+ */
277
+ interface ImportTransport {
278
+ /** Headers, a sample, and a suggested mapping for a freshly chosen file. */
279
+ preview(file: File, context: ImportContext): Promise<ImportPreview>;
280
+ /** Apply a plan to the whole file. */
281
+ commit(file: File, plan: MappingPlan, context: ImportContext): Promise<ImportResult>;
282
+ /**
283
+ * Render a template for download.
284
+ *
285
+ * `fields` is the selection the user actually made — refs with row numbers stripped, one per
286
+ * choice — and `spec` is that selection already expanded against the config. A transport that
287
+ * renders locally wants the spec; one that asks a server wants the selection, because the
288
+ * server has the config and only the choice needs to cross the wire. Passing both means
289
+ * neither has to reconstruct the other: the HTTP transport used to recover the selection from
290
+ * the expanded spec, which worked only because `stripIndices` happens to be exactly the
291
+ * inverse of the expansion. Optional, so an existing implementation keeps compiling.
292
+ */
293
+ template(spec: TemplateSpec, format: TemplateFormat, context: ImportContext, fields?: readonly string[]): Promise<Blob>;
294
+ }
295
+
165
296
  /** Roles that see XXXXXXXXX for masked fields. Presentational only — enforce authz server-side. */
166
297
  declare const MASKED_ROLES: InjectionToken<string[]>;
167
298
  /**
@@ -279,6 +410,46 @@ declare const COMMON_MODULES_REGISTRY: InjectionToken<CommonModuleEntry[]>;
279
410
  * { provide: UPLOAD_HANDLER, useFactory: () => (file: File) => myUploadService.upload(file) }
280
411
  */
281
412
  declare const UPLOAD_HANDLER: InjectionToken<FileUploadHandler>;
413
+ /**
414
+ * Reads an uploaded spreadsheet into headers and positional rows, for the import wizard.
415
+ *
416
+ * Unregistered, the wizard reads CSV with the dependency-free parser in
417
+ * `@dynamic-entity/core`, so it works with nothing provided. Register one to accept `.xlsx`
418
+ * — which spreadsheet library that uses stays the consumer's choice, and this package keeps
419
+ * having no runtime dependency.
420
+ *
421
+ * Rows come back **positional** (`string[][]`) rather than keyed by header, because a real
422
+ * sheet has duplicate headers and blank ones; a header-keyed row silently loses the second
423
+ * of two columns both called "Notes".
424
+ *
425
+ * @example
426
+ * provideNgxDynamicEntity({
427
+ * sheetParser: async file => {
428
+ * const wb = XLSX.read(await file.arrayBuffer());
429
+ * const rows = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], {
430
+ * header: 1, raw: false, defval: '',
431
+ * }) as string[][];
432
+ * return { headers: rows[0] ?? [], rows: rows.slice(1) };
433
+ * },
434
+ * })
435
+ */
436
+ declare const SHEET_PARSER: InjectionToken<SheetParser>;
437
+ /**
438
+ * Where an import's work happens.
439
+ *
440
+ * Unregistered, everything runs in the browser through `@dynamic-entity/core` and nothing
441
+ * touches the network — the wizard is fully usable with no backend. Register one that posts
442
+ * to a server and the same components drive that instead, over the same pure mapping engine,
443
+ * which is what keeps a client-side import and a server-side one from disagreeing about what
444
+ * they produced.
445
+ *
446
+ * The reason to register one is size: the default reads the whole file into memory, which is
447
+ * right for a sheet somebody assembled and wrong for a fifty-thousand-row export.
448
+ *
449
+ * @example
450
+ * { provide: IMPORT_TRANSPORT, useClass: HttpImportTransport }
451
+ */
452
+ declare const IMPORT_TRANSPORT: InjectionToken<ImportTransport>;
282
453
  /**
283
454
  * Backing store for the entity-reference options cache. Defaults to in-memory.
284
455
  * Provide your own to make the cache outlive a page refresh.
@@ -375,13 +546,13 @@ interface NgxDynamicEntityConfig {
375
546
  * Entity-ref option loaders keyed by entity key.
376
547
  * Each receives `{ parentValue, filters, lang }` and may return an array, Promise, or Observable.
377
548
  */
378
- entityRefs?: Record<string, EntityReferenceLoader$1>;
549
+ entityRefs?: Record<string, EntityReferenceLoader>;
379
550
  /**
380
551
  * Named master lists keyed by list name, for fields that set `listName`.
381
552
  * Each may be the values themselves (array/Promise/Observable) or a loader function — prefer
382
553
  * a loader for anything fetched, so an unused list is never loaded.
383
554
  */
384
- lookups?: Record<string, LookupListSource$1>;
555
+ lookups?: Record<string, LookupListSource>;
385
556
  /**
386
557
  * Override the messages shown under invalid fields, keyed by Angular error key. A value is
387
558
  * a string, or a function receiving `(language, error)` when it needs the error's detail.
@@ -414,7 +585,21 @@ interface NgxDynamicEntityConfig {
414
585
  * Ordered steps that upgrade a saved record when `EntityFormConfig.version` moves ahead
415
586
  * of the record's `_configVersion`. Applied to `initialData` before the form is patched.
416
587
  */
417
- migrations?: RecordMigration$1[];
588
+ migrations?: RecordMigration[];
589
+ /**
590
+ * Reads an uploaded spreadsheet for the import wizard, as `{ headers, rows }` with rows
591
+ * positional rather than keyed by header.
592
+ *
593
+ * Omit it and CSV is read by the dependency-free parser in `@dynamic-entity/core`; supply
594
+ * one to accept `.xlsx`, using whichever spreadsheet library you already have.
595
+ */
596
+ sheetParser?: SheetParser;
597
+ /**
598
+ * Where an import's work happens. Omit it and the whole import runs in the browser with no
599
+ * network at all; supply one that posts to a server for files too large to hold in memory.
600
+ * Either way the mapping, coercion and validation are the same pure functions.
601
+ */
602
+ importTransport?: ImportTransport;
418
603
  }
419
604
  /**
420
605
  * Provider function for ngx-dynamic-entity.
@@ -539,7 +724,7 @@ declare class EntityRefRegistryService {
539
724
  * Get the option loader for an entity key.
540
725
  * @returns The loader, or null if none is registered.
541
726
  */
542
- resolve(entityKey: string): EntityReferenceLoader$1 | null;
727
+ resolve(entityKey: string): EntityReferenceLoader | null;
543
728
  has(entityKey: string): boolean;
544
729
  static ɵfac: i0.ɵɵFactoryDeclaration<EntityRefRegistryService, never>;
545
730
  static ɵprov: i0.ɵɵInjectableDeclaration<EntityRefRegistryService>;
@@ -548,7 +733,7 @@ declare class EntityRefRegistryService {
548
733
  interface EntityRefSelection {
549
734
  fieldId: string;
550
735
  /** The chosen option, or `null` when the selection was cleared. */
551
- option: ReferenceOption$1 | null;
736
+ option: ReferenceOption | null;
552
737
  }
553
738
  /**
554
739
  * EntityRefSelectionService — the channel an entity-ref field uses to publish *which record*
@@ -563,7 +748,7 @@ interface EntityRefSelection {
563
748
  declare class EntityRefSelectionService {
564
749
  private readonly subject;
565
750
  readonly selection$: rxjs.Observable<EntityRefSelection>;
566
- emit(fieldId: string, option: ReferenceOption$1 | null): void;
751
+ emit(fieldId: string, option: ReferenceOption | null): void;
567
752
  static ɵfac: i0.ɵɵFactoryDeclaration<EntityRefSelectionService, never>;
568
753
  static ɵprov: i0.ɵɵInjectableDeclaration<EntityRefSelectionService>;
569
754
  }
@@ -588,7 +773,7 @@ declare class CascadeDataService {
588
773
  * Load the options for a field, cascade-filtered against `parentValue`.
589
774
  * Returns `[]` when no loader is registered, or when a cascade child has no parent value yet.
590
775
  */
591
- load(field: NestedFieldConfig, ctx?: ReferenceLoaderContext): Promise<ReferenceOption$1[]>;
776
+ load(field: NestedFieldConfig, ctx?: ReferenceLoaderContext): Promise<ReferenceOption[]>;
592
777
  /**
593
778
  * Warm the cache for every entity a config's cascades depend on, so changing a parent
594
779
  * resolves from memory instead of a round-trip.
@@ -603,7 +788,7 @@ declare class CascadeDataService {
603
788
  * Child options for a parent selection, from cache only — no load is triggered.
604
789
  * Returns `[]` when the entity has not been preloaded.
605
790
  */
606
- getCachedChildOptions(field: NestedFieldConfig, parentValue: unknown, lang?: string): ReferenceOption$1[];
791
+ getCachedChildOptions(field: NestedFieldConfig, parentValue: unknown, lang?: string): ReferenceOption[];
607
792
  /** Forget the preload for one config. The option cache itself is untouched. */
608
793
  clearCache(formConfigId: string): void;
609
794
  /** Every entity-ref field in the config that participates in a cascade. */
@@ -637,9 +822,9 @@ declare class EntityReferenceService {
637
822
  * Options for an entity, from cache when present.
638
823
  * Concurrent callers with the same key share one load.
639
824
  */
640
- load(entityKey: string, ctx?: ReferenceLoaderContext): Promise<ReferenceOption$1[]>;
825
+ load(entityKey: string, ctx?: ReferenceLoaderContext): Promise<ReferenceOption[]>;
641
826
  /** Cached options without triggering a load. `undefined` when not cached. */
642
- peek(entityKey: string, ctx?: ReferenceLoaderContext): ReferenceOption$1[] | undefined;
827
+ peek(entityKey: string, ctx?: ReferenceLoaderContext): ReferenceOption[] | undefined;
643
828
  /**
644
829
  * Display text for a stored reference value, synchronously.
645
830
  * Returns `undefined` when the entity's options have never been loaded.
@@ -785,7 +970,7 @@ declare class RulesEvaluationService {
785
970
  /**
786
971
  * Evaluate rules against form values and optional original baseline values.
787
972
  */
788
- evaluate(rules: FormRule[] | undefined | null, values: Record<string, unknown>, originalValues?: Record<string, unknown>): RuleEvaluationResult$1;
973
+ evaluate(rules: FormRule[] | undefined | null, values: Record<string, unknown>, originalValues?: Record<string, unknown>): RuleEvaluationResult;
789
974
  /**
790
975
  * Filter rules pertinent to a single tab.
791
976
  */
@@ -990,7 +1175,7 @@ declare class DynamicFormComponent implements OnInit, OnChanges, OnDestroy {
990
1175
  /** Previous values, for detecting `patchOnTrue` false→true transitions. */
991
1176
  private previousValues;
992
1177
  handleKeyboardEvent(event: KeyboardEvent): void;
993
- readonly ruleResult: i0.Signal<RuleEvaluationResult>;
1178
+ readonly ruleResult: i0.Signal<ngx_dynamic_entity.RuleEvaluationResult>;
994
1179
  /**
995
1180
  * Critical fields whose value differs from the session baseline.
996
1181
  * These drive the deferred `VALUE_CHANGED` banner — a critical edit is announced once,
@@ -1432,7 +1617,7 @@ declare class DynamicRecordFormComponent implements OnChanges {
1432
1617
  * building a row's FormGroup inside the tab view races the tab's own initialisation, so
1433
1618
  * the row form is kept detached and only pushed into the FormArray on save.
1434
1619
  */
1435
- readonly inlineRowField: i0.WritableSignal<any>;
1620
+ readonly inlineRowField: i0.WritableSignal<NestedFieldConfig>;
1436
1621
  readonly inlineRowIndex: i0.WritableSignal<number>;
1437
1622
  inlineRowForm: FormGroup | null;
1438
1623
  /** `array` fields on the tab currently shown, so the template can list their rows. */
@@ -1667,6 +1852,376 @@ declare class DynamicFieldComponent implements OnChanges, OnDestroy {
1667
1852
  static ɵcmp: i0.ɵɵComponentDeclaration<DynamicFieldComponent, "ngx-dynamic-field", never, { "field": { "alias": "field"; "required": false; }; "control": { "alias": "control"; "required": false; }; "config": { "alias": "config"; "required": false; }; "currentTabId": { "alias": "currentTabId"; "required": false; }; "language": { "alias": "language"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "userRoles": { "alias": "userRoles"; "required": false; }; }, {}, never, never, true, never>;
1668
1853
  }
1669
1854
 
1855
+ /**
1856
+ * sheet-parser.ts — the built-in CSV reader, and the reason it is the only one.
1857
+ *
1858
+ * CSV is what every spreadsheet tool exports and what a text editor can produce, and
1859
+ * `@dynamic-entity/core` parses it with no dependency. Shipping that as the default means the
1860
+ * import wizard works on a fresh `npm install` with nothing registered — `SHEET_PARSER` then
1861
+ * exists for the consumer who *also* wants `.xlsx`, rather than for the consumer who wants
1862
+ * the feature to work at all.
1863
+ *
1864
+ * The alternative was a hard dependency on a spreadsheet library in a package that currently
1865
+ * has none, for a file format many consumers will never see.
1866
+ */
1867
+
1868
+ /**
1869
+ * Read a text file as CSV.
1870
+ *
1871
+ * A binary workbook is refused by name rather than attempted: `.xlsx` is a zip, so reading it
1872
+ * as text yields a header row of mojibake and a mapping screen full of nonsense, which is a
1873
+ * much harder thing to diagnose than being told the format is not supported.
1874
+ */
1875
+ declare const defaultSheetParser: SheetParser;
1876
+
1877
+ declare class LocalImportTransport implements ImportTransport {
1878
+ private readonly registered;
1879
+ /**
1880
+ * One parse per file, not one per call.
1881
+ *
1882
+ * A wizard run reads the same file twice — once to show its headers, once to import it —
1883
+ * and this is the transport whose stated limit is that it holds the file in memory. Doing
1884
+ * the work twice doubled both the parse and the peak memory of the one implementation that
1885
+ * can least afford it.
1886
+ *
1887
+ * A `WeakMap` keyed on the `File` itself: the entry lives exactly as long as something
1888
+ * still holds the file, so choosing a different file drops the old sheet without anything
1889
+ * having to remember to. Keying on a name or a size would collide between two files a user
1890
+ * picked in sequence.
1891
+ */
1892
+ private readonly parsed;
1893
+ /** The registered parser, or the built-in CSV one. */
1894
+ private read;
1895
+ preview(file: File, context: ImportContext): Promise<ImportPreview>;
1896
+ commit(file: File, plan: MappingPlan, context: ImportContext): Promise<ImportResult>;
1897
+ template(spec: TemplateSpec, format: TemplateFormat): Promise<Blob>;
1898
+ static ɵfac: i0.ɵɵFactoryDeclaration<LocalImportTransport, never>;
1899
+ static ɵprov: i0.ɵɵInjectableDeclaration<LocalImportTransport>;
1900
+ }
1901
+
1902
+ /**
1903
+ * http-import-transport.ts — the same wizard, with the work happening on a server.
1904
+ *
1905
+ * The components do not change at all: they already talk to an `ImportTransport`, which was
1906
+ * the point of the seam. Registering this one moves the mapping, coercion and validation from
1907
+ * the tab to a server that runs the *same* `@dynamic-entity/core` functions — so the reason to
1908
+ * do it is size, not behaviour.
1909
+ *
1910
+ * **Built on `fetch`, not `HttpClient`.** This package has no `@angular/common/http` dependency
1911
+ * today, and four requests is not the reason to acquire one — a consumer who wants interceptors
1912
+ * passes their own `fetch`, which is a smaller seam than a dependency.
1913
+ */
1914
+
1915
+ interface HttpImportTransportOptions {
1916
+ /** Where the router is mounted, e.g. `/api/import`. A trailing slash is ignored. */
1917
+ baseUrl: string;
1918
+ /** Supply one to add interceptors, retries, or credentials. Defaults to the global `fetch`. */
1919
+ fetch?: typeof fetch;
1920
+ /** Headers on every request — an `Authorization`, a tenant id. Never a `Content-Type`. */
1921
+ headers?: Record<string, string> | (() => Record<string, string>);
1922
+ /**
1923
+ * Where a warning goes. Defaults to `console.warn`.
1924
+ *
1925
+ * There is exactly one warning, and it is the engine-version mismatch below — which is a
1926
+ * developer's problem, not a user's, so it does not belong in the wizard's error banner.
1927
+ */
1928
+ warn?: (message: string) => void;
1929
+ }
1930
+ /**
1931
+ * Not `@Injectable`, and registered by value rather than by class.
1932
+ *
1933
+ * Its constructor takes options rather than dependencies, so there is nothing for an injector
1934
+ * to resolve — and a consumer who wants two of them, pointed at two backends, gets that for
1935
+ * free.
1936
+ */
1937
+ declare class HttpImportTransport implements ImportTransport {
1938
+ private readonly options;
1939
+ constructor(options: HttpImportTransportOptions);
1940
+ private get base();
1941
+ private get send();
1942
+ private entityOf;
1943
+ /**
1944
+ * Warn when the server's engine is not the one this bundle was built against.
1945
+ *
1946
+ * The two halves are separately deployed and may differ. `MappingPlan.configVersion` catches
1947
+ * *config* drift and says nothing about *engine* drift, so "one engine, both sides" is only
1948
+ * true if something checks — and this is the something. A warning rather than a refusal: a
1949
+ * patch release is not a reason to stop somebody importing.
1950
+ */
1951
+ private checkEngine;
1952
+ preview(file: File, context: ImportContext): Promise<ImportPreview>;
1953
+ commit(file: File, plan: MappingPlan, context: ImportContext): Promise<ImportResult>;
1954
+ template(spec: TemplateSpec, format: TemplateFormat, context: ImportContext, fields?: readonly string[]): Promise<Blob>;
1955
+ }
1956
+ /**
1957
+ * Register the HTTP transport.
1958
+ *
1959
+ * ```ts
1960
+ * provideNgxDynamicEntity({}),
1961
+ * provideHttpImportTransport({ baseUrl: '/api/import' }),
1962
+ * ```
1963
+ *
1964
+ * A plain `Provider` rather than `EnvironmentProviders`, so it composes inside a component's
1965
+ * or a route's `providers` array as well as at the root — which is how a consumer points one
1966
+ * part of an app at a different backend.
1967
+ */
1968
+ declare function provideHttpImportTransport(options: HttpImportTransportOptions): Provider;
1969
+
1970
+ type Step = 'upload' | 'map' | 'review' | 'done';
1971
+ /**
1972
+ * The import wizard: choose a file, match its columns, look at what that produced, commit.
1973
+ *
1974
+ * It holds no import logic of its own. Everything it does goes through an `ImportTransport`,
1975
+ * which by default is the in-browser one built on `@dynamic-entity/core` — so the wizard
1976
+ * works with no backend, and a consumer who registers a server transport gets the same
1977
+ * screens over the same mapping rules rather than a second implementation of them.
1978
+ *
1979
+ * The review step exists because the expensive mistake here is not a file that fails to
1980
+ * import, it is one that imports *successfully into the wrong fields*. That is invisible in a
1981
+ * summary and obvious in five rendered rows.
1982
+ */
1983
+ declare class EntityImportComponent {
1984
+ config: EntityFormConfig;
1985
+ /**
1986
+ * The rules the form is rendered with.
1987
+ *
1988
+ * Pass them, or the import checks field validators only — which is not what the form
1989
+ * enforces, and a required field hidden by a rule will fail every row.
1990
+ */
1991
+ rules?: readonly FormRule[];
1992
+ /**
1993
+ * Named lists, overriding what the wizard already resolves from `LOOKUP_REGISTRY`.
1994
+ *
1995
+ * Not something a host has to supply: every `listName` a config mentions is loaded from the
1996
+ * registry the renderer is already holding. Pass this only to override one.
1997
+ */
1998
+ lookups?: ImportLookups;
1999
+ language: string;
2000
+ /** Format asked of the transport when a template is downloaded. */
2001
+ templateFormat: TemplateFormat;
2002
+ /** The finished import, once it has run. */
2003
+ readonly importComplete: EventEmitter<ImportResult>;
2004
+ /** A template the transport produced, for a host that would rather save it its own way. */
2005
+ readonly templateReady: EventEmitter<Blob>;
2006
+ protected readonly ui: UiTextService;
2007
+ protected readonly steps: Step[];
2008
+ protected readonly step: i0.WritableSignal<Step>;
2009
+ protected readonly busy: i0.WritableSignal<boolean>;
2010
+ protected readonly problem: i0.WritableSignal<string>;
2011
+ protected readonly headers: i0.WritableSignal<string[]>;
2012
+ protected readonly sample: i0.WritableSignal<string[][]>;
2013
+ protected readonly rowCount: i0.WritableSignal<number>;
2014
+ protected readonly fileName: i0.WritableSignal<string>;
2015
+ protected readonly plan: i0.WritableSignal<MappingPlan>;
2016
+ protected readonly result: i0.WritableSignal<ImportResult>;
2017
+ private readonly registered;
2018
+ private readonly local;
2019
+ private readonly lookupRegistry;
2020
+ private file;
2021
+ /** Named lists resolved from `LOOKUP_REGISTRY`, merged under the `lookups` input. */
2022
+ protected readonly resolvedLookups: i0.WritableSignal<ImportLookups>;
2023
+ private get transport();
2024
+ private context;
2025
+ /**
2026
+ * Load every named list this config's fields refer to.
2027
+ *
2028
+ * `@dynamic-entity/core` is framework-agnostic and cannot reach `LOOKUP_REGISTRY`, so
2029
+ * `coerceCell` takes the values as an argument. That is a reason for *core* to ask, not a
2030
+ * reason for the host to be asked — this package is holding the registry, and making the
2031
+ * consumer re-supply what the library already has is how a `listName` column ends up
2032
+ * storing the raw text `"Gold"` instead of the option object. Such a record renders
2033
+ * correctly and then matches no rule, which is the exact failure the option-shape contract
2034
+ * exists to prevent.
2035
+ *
2036
+ * The `lookups` input still wins where it names a list, so a host can override one without
2037
+ * having to provide all of them.
2038
+ */
2039
+ private loadLookups;
2040
+ protected stepLabel(step: Step): string;
2041
+ protected chooseFile(event: Event): Promise<void>;
2042
+ protected commit(): Promise<void>;
2043
+ protected downloadTemplate(fields: string[]): Promise<void>;
2044
+ /**
2045
+ * How many records were written.
2046
+ *
2047
+ * `records.length` for the in-browser transport, which returns every record it built, and
2048
+ * `imported` for a server one, which streamed the file precisely so that fifty thousand
2049
+ * records never had to exist at once and therefore has a number rather than a list. Reading
2050
+ * `records.length` alone would report a successful import of nothing.
2051
+ */
2052
+ protected importedCount(result: ImportResult): number;
2053
+ /**
2054
+ * How many distinct rows failed, not how many problems they had between them.
2055
+ *
2056
+ * `failed` when the transport reports it, and the distinct rows in `errors` only when it does
2057
+ * not. Counting the errors alone was wrong the moment a transport started capping them: a run
2058
+ * of a thousand rows where two hundred failed said **seven**, because seven was how many
2059
+ * distinct rows fitted inside the first twenty retained problems. This is the number a user
2060
+ * acts on — which rows do I go and fix — so a confident wrong answer is the worst of the
2061
+ * available ones. The in-browser transport returns every error, so counting them there is
2062
+ * exact and `failed` is absent.
2063
+ */
2064
+ protected failedRowCount(result: ImportResult): number;
2065
+ protected restart(): void;
2066
+ static ɵfac: i0.ɵɵFactoryDeclaration<EntityImportComponent, never>;
2067
+ static ɵcmp: i0.ɵɵComponentDeclaration<EntityImportComponent, "ngx-entity-import", never, { "config": { "alias": "config"; "required": true; }; "rules": { "alias": "rules"; "required": false; }; "lookups": { "alias": "lookups"; "required": false; }; "language": { "alias": "language"; "required": false; }; "templateFormat": { "alias": "templateFormat"; "required": false; }; }, { "importComplete": "importComplete"; "templateReady": "templateReady"; }, never, never, true, never>;
2068
+ }
2069
+
2070
+ /** One target field, and which of the sheet's columns feeds it. */
2071
+ interface MappingRow {
2072
+ column: ImportColumn;
2073
+ /** Index into the sheet's headers, or `null` for "not imported". */
2074
+ source: number | null;
2075
+ guessed: boolean;
2076
+ }
2077
+ /**
2078
+ * Match the sheet's columns to the config's fields.
2079
+ *
2080
+ * The table is driven by the **target fields**, not by the sheet's columns, which is the
2081
+ * choice that decides how the screen reads. A user is answering "where does Start Date come
2082
+ * from?", which they can do; column-first would ask "what is column G?", which they often
2083
+ * cannot, and would make a sheet with forty irrelevant columns forty questions long.
2084
+ *
2085
+ * A selection is a **column index**, never header text. Two columns headed "Notes" are two
2086
+ * different answers, and text cannot tell them apart.
2087
+ */
2088
+ declare class ImportMapperComponent implements OnChanges {
2089
+ config: EntityFormConfig;
2090
+ headers: readonly string[];
2091
+ /** The starting point, normally `suggestMapping`'s output. */
2092
+ plan: MappingPlan | null;
2093
+ language: string;
2094
+ /** Emitted whenever the user changes a selection. */
2095
+ readonly planChange: EventEmitter<MappingPlan>;
2096
+ protected readonly ui: UiTextService;
2097
+ protected readonly rows: i0.WritableSignal<MappingRow[]>;
2098
+ /**
2099
+ * How many fields a spreadsheet cannot carry at all.
2100
+ *
2101
+ * Shown here as well as in the template picker, because a user who already had a sheet
2102
+ * skipped that screen — and this is the only place they would otherwise find out that an
2103
+ * attachment column was never going to arrive.
2104
+ */
2105
+ protected readonly unsupported: i0.WritableSignal<number>;
2106
+ ngOnChanges(): void;
2107
+ protected mappedCount(): number;
2108
+ protected missingRequired(): number;
2109
+ protected choose(row: MappingRow, source: number | null): void;
2110
+ /** The current selections as the plan that will be applied. */
2111
+ private toPlan;
2112
+ static ɵfac: i0.ɵɵFactoryDeclaration<ImportMapperComponent, never>;
2113
+ static ɵcmp: i0.ɵɵComponentDeclaration<ImportMapperComponent, "ngx-import-mapper", never, { "config": { "alias": "config"; "required": true; }; "headers": { "alias": "headers"; "required": false; }; "plan": { "alias": "plan"; "required": false; }; "language": { "alias": "language"; "required": false; }; }, { "planChange": "planChange"; }, never, never, true, never>;
2114
+ }
2115
+
2116
+ /** One cell, already turned into the text the record will show. */
2117
+ interface PreviewCell {
2118
+ text: string;
2119
+ failed: boolean;
2120
+ }
2121
+ /** A row of cells, plus why the import will refuse it — if it will. */
2122
+ interface PreviewRow {
2123
+ cells: PreviewCell[];
2124
+ /** Empty when the row will be imported. */
2125
+ rejections: string[];
2126
+ }
2127
+ /**
2128
+ * The first few rows as the record will hold them, not as the sheet holds them.
2129
+ *
2130
+ * Every cell goes through the same `coerceCell` the import will use and then through
2131
+ * `formatDisplayValue`, which is what the renderer uses to show a stored value. So a
2132
+ * `dropdown` cell reading `Aktiv` shows as `Active` here if that is the option it resolved
2133
+ * to, and a cell that will fail shows its reason before anyone commits to anything.
2134
+ *
2135
+ * Showing the raw text instead would be easier and would preview the wrong thing: the point
2136
+ * of this screen is to catch a mapping that is subtly wrong, and a column of unchanged text
2137
+ * looks correct whatever field it was pointed at.
2138
+ *
2139
+ * **The rows are also run through the real import**, not just through coercion. `coerceCell`
2140
+ * alone catches a cell that will not parse and says nothing about a row that will be rejected
2141
+ * — a missing required field, a rule that forbids the combination — so a heading promising
2142
+ * "as they will be saved" was showing rows that would never be saved at all, looking exactly
2143
+ * like the ones that would. `applyMapping` over the sample answers that with the same engine
2144
+ * the commit uses, rather than a second opinion about what counts as valid.
2145
+ *
2146
+ * The whole grid is built once per input change rather than from template method calls.
2147
+ * Deriving the config's columns inside a `@for` means re-walking the entire schema for every
2148
+ * cell on every change-detection pass, which is a lot of work to produce five rows.
2149
+ */
2150
+ declare class ImportPreviewComponent implements OnChanges {
2151
+ config: EntityFormConfig;
2152
+ plan: MappingPlan;
2153
+ rows: readonly (readonly string[])[];
2154
+ lookups?: ImportLookups;
2155
+ /** The same rules the commit will apply; without them a rule-driven rejection is invisible. */
2156
+ rules?: readonly FormRule[];
2157
+ language: string;
2158
+ protected readonly ui: UiTextService;
2159
+ /** The columns this plan fills, in the plan's own order. */
2160
+ protected readonly columns: i0.WritableSignal<ImportColumn[]>;
2161
+ protected readonly grid: i0.WritableSignal<PreviewRow[]>;
2162
+ ngOnChanges(): void;
2163
+ static ɵfac: i0.ɵɵFactoryDeclaration<ImportPreviewComponent, never>;
2164
+ static ɵcmp: i0.ɵɵComponentDeclaration<ImportPreviewComponent, "ngx-import-preview", never, { "config": { "alias": "config"; "required": true; }; "plan": { "alias": "plan"; "required": true; }; "rows": { "alias": "rows"; "required": false; }; "lookups": { "alias": "lookups"; "required": false; }; "rules": { "alias": "rules"; "required": false; }; "language": { "alias": "language"; "required": false; }; }, {}, never, never, true, never>;
2165
+ }
2166
+
2167
+ /** One field offered for the template, with the guidance that belongs to it. */
2168
+ interface TemplateChoice {
2169
+ /** The ref with any row number removed, so a repeating field is offered once. */
2170
+ ref: string;
2171
+ header: string;
2172
+ note: string;
2173
+ selected: boolean;
2174
+ }
2175
+ /**
2176
+ * Pick the fields a template should carry, then download it.
2177
+ *
2178
+ * The guidance for each field is shown **here**, beside the checkbox, rather than written
2179
+ * into the file as a second header row. CSV has one header row and no concept of a second,
2180
+ * so a guidance row inside the file would come back as a record on re-import and fail row 2
2181
+ * of every import the user ever ran.
2182
+ *
2183
+ * A repeating field is offered once, not once per row number: "Contacts / Name" is a decision,
2184
+ * and "Contacts / Name 1", "Contacts / Name 2", "Contacts / Name 3" is the same decision
2185
+ * asked three times.
2186
+ */
2187
+ declare class ImportTemplateComponent implements OnChanges {
2188
+ config: EntityFormConfig;
2189
+ language: string;
2190
+ /** The refs to include, with row numbers stripped — the caller builds the spec. */
2191
+ readonly download: EventEmitter<string[]>;
2192
+ protected readonly ui: UiTextService;
2193
+ protected readonly choices: i0.WritableSignal<TemplateChoice[]>;
2194
+ protected readonly unsupported: i0.WritableSignal<UnsupportedColumn[]>;
2195
+ ngOnChanges(): void;
2196
+ protected selectedCount(): number;
2197
+ protected selectedRefs(): string[];
2198
+ protected toggle(ref: string): void;
2199
+ protected selectAll(selected: boolean): void;
2200
+ static ɵfac: i0.ɵɵFactoryDeclaration<ImportTemplateComponent, never>;
2201
+ static ɵcmp: i0.ɵɵComponentDeclaration<ImportTemplateComponent, "ngx-import-template", never, { "config": { "alias": "config"; "required": true; }; "language": { "alias": "language"; "required": false; }; }, { "download": "download"; }, never, never, true, never>;
2202
+ }
2203
+
2204
+ /**
2205
+ * The rows an import could not take, and why.
2206
+ *
2207
+ * Grouped by row rather than listed flat: a row with four problems is one row to go and fix,
2208
+ * and four separate lines invite the reader to think there are four. The row number is the
2209
+ * one the spreadsheet shows in its gutter — header counted as row 1 — because that is the
2210
+ * number they will scroll to.
2211
+ */
2212
+ declare class ImportErrorsComponent {
2213
+ errors: readonly ImportRowError[];
2214
+ language: string;
2215
+ protected readonly ui: UiTextService;
2216
+ /** Row number → its problems, in the order the rows appear in the file. */
2217
+ protected grouped(): {
2218
+ row: number;
2219
+ problems: ImportRowError[];
2220
+ }[];
2221
+ static ɵfac: i0.ɵɵFactoryDeclaration<ImportErrorsComponent, never>;
2222
+ static ɵcmp: i0.ɵɵComponentDeclaration<ImportErrorsComponent, "ngx-import-errors", never, { "errors": { "alias": "errors"; "required": false; }; "language": { "alias": "language"; "required": false; }; }, {}, never, never, true, never>;
2223
+ }
2224
+
1670
2225
  /**
1671
2226
  * What a field component must accept to be registered with `provideFieldTypes`.
1672
2227
  *
@@ -2312,7 +2867,7 @@ declare class DropdownFieldComponent {
2312
2867
  readonly: boolean;
2313
2868
  masked: boolean;
2314
2869
  /** Inline `options`, or the field's named list resolved through the registry (§6.3). */
2315
- readonly options: i0.WritableSignal<DropdownOption[]>;
2870
+ readonly options: i0.WritableSignal<ngx_dynamic_entity.LocalizedText[]>;
2316
2871
  readonly compareFn: (o1: unknown, o2: unknown) => boolean;
2317
2872
  isObjectVal(option: DropdownOption): boolean;
2318
2873
  get label(): string;
@@ -2357,7 +2912,7 @@ declare class RadioFieldComponent {
2357
2912
  readonly: boolean;
2358
2913
  masked: boolean;
2359
2914
  /** Inline `options`, or the field's named list resolved through the registry (§6.3). */
2360
- readonly options: i0.WritableSignal<DropdownOption[]>;
2915
+ readonly options: i0.WritableSignal<ngx_dynamic_entity.LocalizedText[]>;
2361
2916
  get label(): string;
2362
2917
  getOptStoredVal(option: DropdownOption): unknown;
2363
2918
  /**
@@ -2415,7 +2970,7 @@ declare class MultiSelectFieldComponent {
2415
2970
  readonly: boolean;
2416
2971
  masked: boolean;
2417
2972
  /** Inline `options`, or the field's named list resolved through the registry (§6.3). */
2418
- readonly options: i0.WritableSignal<DropdownOption[]>;
2973
+ readonly options: i0.WritableSignal<ngx_dynamic_entity.LocalizedText[]>;
2419
2974
  readonly compareFn: (o1: unknown, o2: unknown) => boolean;
2420
2975
  isObjectVal(option: DropdownOption): boolean;
2421
2976
  get label(): string;
@@ -2641,5 +3196,5 @@ declare class FileFieldComponent {
2641
3196
  static ɵcmp: i0.ɵɵComponentDeclaration<FileFieldComponent, "ngx-file-field", never, { "field": { "alias": "field"; "required": false; }; "control": { "alias": "control"; "required": false; }; "language": { "alias": "language"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "masked": { "alias": "masked"; "required": false; }; }, {}, never, never, true, never>;
2642
3197
  }
2643
3198
 
2644
- export { ASYNC_VALIDATOR_REGISTRY, ArrayFieldComponent, BooleanFieldComponent, COMMON_MODULES_REGISTRY, CONFIG_SOURCE, CascadeDataService, CheckboxFieldComponent, ConfigSourceService, CurrencyFieldComponent, DEFAULT_UI_TEXT, DEFAULT_VALIDATION_MESSAGES, DateFieldComponent, DateTimeFieldComponent, DropdownFieldComponent, DynamicFieldComponent, DynamicFormComponent, DynamicRecordFormComponent, ENTITY_REF_CACHE_STORE, ENTITY_REF_REGISTRY, EmailFieldComponent, EntityRefFieldComponent, EntityRefRegistryService, EntityRefSelectionService, EntityReferenceService, FIELD_TYPE_REGISTRY, FIELD_TYPE_SETS, FieldRegistryService, FileFieldComponent, GroupFieldComponent, HOOK_REGISTRY, HookRegistryService, ImageFieldComponent, InMemoryEntityRefCacheStore, LOOKUP_REGISTRY, LookupRegistryService, MARKDOWN_RENDERER, MASKED_PLACEHOLDER, MASKED_ROLES, MarkdownFieldComponent, MonthYearFieldComponent, MultiSelectFieldComponent, NumberFieldComponent, PasswordFieldComponent, RECORD_MIGRATIONS, RadioFieldComponent, RbacService, RulesEvaluationService, SYSTEM_DEFAULT_CAN_EDIT, TextFieldComponent, TextareaFieldComponent, TimeFieldComponent, UI_TEXT, UPLOAD_HANDLER, UiTextService, VALIDATION_MESSAGES, VALIDATOR_REGISTRY, ValidationMessagesService, ValidatorRegistryService, builtInFieldTypes, fieldDescribedBy, fieldDomId, nextFieldInstanceId, provideBuiltInFieldTypes, provideFieldTypes, provideNgxDynamicEntity, refreshChoiceOptions, resolveUiText };
2645
- export type { ChoiceOptionHost, ConfigSourceHandler, DynamicFieldComponentContract, EntityRefCacheStore, EntityRefSelection, HookFn, InvalidField, MessageOrderEntry, NgxDynamicEntityConfig, UiTextKey, UiTextOverrides, UiTextOverridesFor, UiTextParams, UiTextResolver, UiTextResolverFor, UiTextValue, ValidationMessage };
3199
+ export { ASYNC_VALIDATOR_REGISTRY, ArrayFieldComponent, BooleanFieldComponent, COMMON_MODULES_REGISTRY, CONFIG_SOURCE, CascadeDataService, CheckboxFieldComponent, ConfigSourceService, CurrencyFieldComponent, DEFAULT_UI_TEXT, DEFAULT_VALIDATION_MESSAGES, DateFieldComponent, DateTimeFieldComponent, DropdownFieldComponent, DynamicFieldComponent, DynamicFormComponent, DynamicRecordFormComponent, ENTITY_REF_CACHE_STORE, ENTITY_REF_REGISTRY, EmailFieldComponent, EntityImportComponent, EntityRefFieldComponent, EntityRefRegistryService, EntityRefSelectionService, EntityReferenceService, FIELD_TYPE_REGISTRY, FIELD_TYPE_SETS, FieldRegistryService, FileFieldComponent, GroupFieldComponent, HOOK_REGISTRY, HookRegistryService, HttpImportTransport, IMPORT_TRANSPORT, ImageFieldComponent, ImportErrorsComponent, ImportMapperComponent, ImportPreviewComponent, ImportTemplateComponent, InMemoryEntityRefCacheStore, LOOKUP_REGISTRY, LocalImportTransport, LookupRegistryService, MARKDOWN_RENDERER, MASKED_PLACEHOLDER, MASKED_ROLES, MarkdownFieldComponent, MonthYearFieldComponent, MultiSelectFieldComponent, NumberFieldComponent, PasswordFieldComponent, RECORD_MIGRATIONS, RadioFieldComponent, RbacService, RulesEvaluationService, SHEET_PARSER, SYSTEM_DEFAULT_CAN_EDIT, TextFieldComponent, TextareaFieldComponent, TimeFieldComponent, UI_TEXT, UPLOAD_HANDLER, UiTextService, VALIDATION_MESSAGES, VALIDATOR_REGISTRY, ValidationMessagesService, ValidatorRegistryService, builtInFieldTypes, defaultSheetParser, fieldDescribedBy, fieldDomId, nextFieldInstanceId, provideBuiltInFieldTypes, provideFieldTypes, provideHttpImportTransport, provideNgxDynamicEntity, refreshChoiceOptions, resolveUiText };
3200
+ export type { ChoiceOptionHost, ConfigSourceHandler, DynamicFieldComponentContract, EntityRefCacheStore, EntityRefSelection, HookFn, HttpImportTransportOptions, ImportContext, ImportPreview, ImportTransport, InvalidField, MessageOrderEntry, NgxDynamicEntityConfig, SheetParser, TemplateFormat, UiTextKey, UiTextOverrides, UiTextOverridesFor, UiTextParams, UiTextResolver, UiTextResolverFor, UiTextValue, ValidationMessage };