@dynamic-entity/server 2.2.0 → 2.3.1

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,4 +1,4 @@
1
- import { ImportRowError, ConfigProblem, EntityFormConfig, MappingPlan, FormRule, ImportLookups, TemplateSpec } from '@dynamic-entity/core';
1
+ import { CsvDelimiter, ImportRowError, ConfigProblem, EntityFormConfig, MappingPlan, FormRule, ImportLookups, TemplateSpec } from '@dynamic-entity/core';
2
2
  import { Writable } from 'node:stream';
3
3
 
4
4
  /**
@@ -126,7 +126,8 @@ declare function destroySource(source: unknown): void;
126
126
  * 1. **Format is decided by content, never by the filename.** `.csv` is client-controlled and
127
127
  * an attacker who wants a zip parsed will name their zip `.csv`. The first four bytes are
128
128
  * not. Both readers then meet the same row guards, so a workbook cannot buy itself a
129
- * larger sheet by being a workbook.
129
+ * larger sheet by being a workbook. The name is consulted only after the bytes have said
130
+ * the file is text, and only for its separator: `.tsv` means tab, as it does in the browser.
130
131
  * 2. **Rows arrive one at a time.** Nothing here ever holds the sheet. `rows` is an
131
132
  * `AsyncIterable`, so a consumer that stops pulling stops the read.
132
133
  *
@@ -143,10 +144,17 @@ interface SheetSource {
143
144
  headers: string[];
144
145
  /** Data rows, one at a time. Pulling stops the read; not pulling stops it too. */
145
146
  rows: AsyncIterable<unknown[]>;
147
+ /** The field separator a CSV turned out to use. Absent for a workbook. */
148
+ delimiter?: CsvDelimiter;
146
149
  }
147
150
  interface ReadSheetOptions {
148
151
  stream: ByteSource;
149
- /** Display only — it names the file in an error message and decides nothing. */
152
+ /**
153
+ * The client's name for the upload. It never decides the format — the bytes do. Once they
154
+ * have said the file is text, a name ending in `.tsv` fixes the separator as a tab, the way
155
+ * the browser reads one; any other name leaves the separator to `detectDelimiter`. A wrong
156
+ * name can at worst split a text file oddly, which the preview shows before anything imports.
157
+ */
150
158
  filename?: string;
151
159
  limits?: Partial<ImportLimits>;
152
160
  }
@@ -217,6 +225,11 @@ interface RunImportOptions {
217
225
  /** Values for every `listName` the config mentions. A server has no `LOOKUP_REGISTRY`. */
218
226
  lookups?: ImportLookups;
219
227
  lang?: string;
228
+ /**
229
+ * The decimal mark in number cells. Defaults to `,` for a CSV that uses `;` between fields,
230
+ * because that is what Excel writes in exactly those locales, and `.` otherwise.
231
+ */
232
+ decimal?: '.' | ',';
220
233
  /** Omit to validate without writing: the identical pipeline, every error, nothing stored. */
221
234
  onBatch?: OnBatch;
222
235
  limits?: Partial<ImportLimits>;
@@ -279,6 +292,10 @@ interface SheetPreview {
279
292
  /** Data rows in the file, which the whole stream is read to count. */
280
293
  rowCount: number;
281
294
  format: SheetFormat;
295
+ /** The field separator a CSV uses. Absent for a workbook. */
296
+ delimiter?: CsvDelimiter;
297
+ /** Rows of each repeating field the suggestion was sized for — see `arrayBoundFor`. */
298
+ arrayBound: number;
282
299
  }
283
300
  /**
284
301
  * Headers, a sample, and a suggested mapping.