@dynamic-entity/server 2.1.1 → 2.3.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/README.md +12 -1
- package/express.d.mts +1 -1
- package/express.d.ts +1 -1
- package/express.js +48 -21
- package/express.js.map +1 -1
- package/express.mjs +49 -22
- package/express.mjs.map +1 -1
- package/index.d.mts +4 -22
- package/index.d.ts +4 -22
- package/index.js +51 -23
- package/index.js.map +1 -1
- package/index.mjs +49 -23
- package/index.mjs.map +1 -1
- package/package.json +2 -2
- package/{write-template-C2dCEp48.d.mts → write-template-B6KIIBRw.d.mts} +12 -1
- package/{write-template-C2dCEp48.d.ts → write-template-B6KIIBRw.d.ts} +12 -1
package/express.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deriveImportColumns, buildTemplateSpec, CORE_VERSION, suggestMapping, toCsv, escapeFormula, validateMappingPlan, applyMapping,
|
|
1
|
+
import { deriveImportColumns, buildTemplateSpec, CORE_VERSION, cellText, arrayBoundFor, suggestMapping, decimalMarkFor, toCsv, escapeFormula, validateMappingPlan, applyMapping, completeFirstLine, padRow, detectDelimiter, createCsvReader } from '@dynamic-entity/core';
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import Busboy from 'busboy';
|
|
4
4
|
import ExcelJS from 'exceljs';
|
|
@@ -235,17 +235,6 @@ function readPlanField(fields) {
|
|
|
235
235
|
throw new ImportError("INVALID_PLAN", "The mapping plan is not valid JSON.", { cause });
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
-
|
|
239
|
-
// src/cell-text.ts
|
|
240
|
-
function sampleText(raw) {
|
|
241
|
-
if (raw === null || raw === void 0) return "";
|
|
242
|
-
if (raw instanceof Date) {
|
|
243
|
-
if (Number.isNaN(raw.getTime())) return "";
|
|
244
|
-
const iso = raw.toISOString();
|
|
245
|
-
return raw.getTime() % 864e5 === 0 ? iso.slice(0, 10) : iso;
|
|
246
|
-
}
|
|
247
|
-
return typeof raw === "string" ? raw : String(raw);
|
|
248
|
-
}
|
|
249
238
|
var LOCAL_HEADER = 67324752;
|
|
250
239
|
var CENTRAL_HEADER = 33639248;
|
|
251
240
|
var END_OF_CENTRAL = 101010256;
|
|
@@ -562,6 +551,12 @@ async function guardZip(source, limits) {
|
|
|
562
551
|
}
|
|
563
552
|
}
|
|
564
553
|
if (!sheet) {
|
|
554
|
+
if (!kept.has("xl/workbook.xml")) {
|
|
555
|
+
throw new ImportError(
|
|
556
|
+
"UNSUPPORTED_FORMAT",
|
|
557
|
+
"This is a zip archive that is not an Excel workbook. Upload an .xlsx or .csv file."
|
|
558
|
+
);
|
|
559
|
+
}
|
|
565
560
|
throw new ImportError("MALFORMED_FILE", "This workbook has no worksheet.");
|
|
566
561
|
}
|
|
567
562
|
const ordered = [...WANTED, sheet.name].map((wanted) => kept.get(wanted)).filter((entry) => entry !== void 0);
|
|
@@ -656,13 +651,36 @@ function detectFormat(head) {
|
|
|
656
651
|
}
|
|
657
652
|
return "csv";
|
|
658
653
|
}
|
|
659
|
-
async function* csvRows(stream) {
|
|
654
|
+
async function* csvRows(stream, limits, found) {
|
|
660
655
|
const decoder = new TextDecoder("utf-8");
|
|
661
|
-
const
|
|
656
|
+
const maxHeader = limits.maxCellLength * limits.maxColumns + limits.maxColumns;
|
|
657
|
+
let held = "";
|
|
658
|
+
let reader = null;
|
|
659
|
+
const open = (headerLine) => {
|
|
660
|
+
found.delimiter = detectDelimiter(headerLine);
|
|
661
|
+
return createCsvReader({ delimiter: found.delimiter });
|
|
662
|
+
};
|
|
662
663
|
for await (const chunk of stream) {
|
|
663
|
-
|
|
664
|
+
const text = decoder.decode(chunk, { stream: true });
|
|
665
|
+
if (!reader) {
|
|
666
|
+
held += text;
|
|
667
|
+
const line = completeFirstLine(held);
|
|
668
|
+
if (line === null) {
|
|
669
|
+
if (held.length > maxHeader) {
|
|
670
|
+
throw new ImportError("SHEET_TOO_LARGE", "The header row is longer than any row may be.");
|
|
671
|
+
}
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
reader = open(line);
|
|
675
|
+
for (const row of reader.push(held)) yield row;
|
|
676
|
+
held = "";
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
for (const row of reader.push(text)) yield row;
|
|
664
680
|
}
|
|
665
|
-
|
|
681
|
+
const rest = held + decoder.decode();
|
|
682
|
+
reader ??= open(completeFirstLine(rest) ?? rest);
|
|
683
|
+
for (const row of reader.push(rest)) yield row;
|
|
666
684
|
for (const row of reader.end()) yield row;
|
|
667
685
|
}
|
|
668
686
|
async function* guardRows(rows, width, limits) {
|
|
@@ -697,10 +715,11 @@ async function readSheet(options) {
|
|
|
697
715
|
const counted = limitBytes(toByteStream(options.stream), limits.maxBytes);
|
|
698
716
|
const { head, stream } = await peek(counted, MAGIC_BYTES);
|
|
699
717
|
const format = detectFormat(head);
|
|
700
|
-
const
|
|
718
|
+
const found = {};
|
|
719
|
+
const rows = format === "xlsx" ? xlsxRows(stream, limits) : csvRows(stream, limits, found);
|
|
701
720
|
const iterator = rows[Symbol.asyncIterator]();
|
|
702
721
|
const first = await iterator.next();
|
|
703
|
-
const headers = first.done ? [] : first.value.map(
|
|
722
|
+
const headers = first.done ? [] : first.value.map(cellText);
|
|
704
723
|
if (headers.length > limits.maxColumns) {
|
|
705
724
|
throw new ImportError(
|
|
706
725
|
"SHEET_TOO_LARGE",
|
|
@@ -711,7 +730,8 @@ async function readSheet(options) {
|
|
|
711
730
|
return {
|
|
712
731
|
format,
|
|
713
732
|
headers,
|
|
714
|
-
rows: closing(guardRows(remaining, headers.length, limits), options.stream)
|
|
733
|
+
rows: closing(guardRows(remaining, headers.length, limits), options.stream),
|
|
734
|
+
...found.delimiter ? { delimiter: found.delimiter } : {}
|
|
715
735
|
};
|
|
716
736
|
} catch (error) {
|
|
717
737
|
destroySource(options.stream);
|
|
@@ -765,6 +785,7 @@ async function runImport(options) {
|
|
|
765
785
|
filename: options.filename,
|
|
766
786
|
limits
|
|
767
787
|
});
|
|
788
|
+
const decimal = options.decimal ?? decimalMarkFor(sheet.delimiter);
|
|
768
789
|
const errors = [];
|
|
769
790
|
let errorCount = 0;
|
|
770
791
|
let imported = 0;
|
|
@@ -780,6 +801,7 @@ async function runImport(options) {
|
|
|
780
801
|
batch = [];
|
|
781
802
|
const result = applyMapping(rows, plan, config, {
|
|
782
803
|
lang: options.lang,
|
|
804
|
+
decimal,
|
|
783
805
|
rules: options.rules,
|
|
784
806
|
lookups: options.lookups,
|
|
785
807
|
firstRowNumber: firstRow
|
|
@@ -842,19 +864,22 @@ async function previewSheet(options) {
|
|
|
842
864
|
try {
|
|
843
865
|
for await (const row of sheet.rows) {
|
|
844
866
|
rowCount++;
|
|
845
|
-
if (sample.length < limits.sampleRows) sample.push(row.map(
|
|
867
|
+
if (sample.length < limits.sampleRows) sample.push(row.map(cellText));
|
|
846
868
|
}
|
|
847
869
|
} catch (error) {
|
|
848
870
|
destroySource(options.stream);
|
|
849
871
|
throw error;
|
|
850
872
|
}
|
|
851
|
-
const
|
|
873
|
+
const arrayBound = arrayBoundFor(sheet.headers, options.config, null, options.lang);
|
|
874
|
+
const { columns } = deriveImportColumns(options.config, { lang: options.lang, maxArrayRows: arrayBound });
|
|
852
875
|
return {
|
|
853
876
|
headers: sheet.headers,
|
|
854
877
|
sample,
|
|
855
878
|
rowCount,
|
|
856
879
|
suggestion: suggestMapping(sheet.headers, columns, options.config?.entity ?? ""),
|
|
857
|
-
format: sheet.format
|
|
880
|
+
format: sheet.format,
|
|
881
|
+
...sheet.delimiter ? { delimiter: sheet.delimiter } : {},
|
|
882
|
+
arrayBound
|
|
858
883
|
};
|
|
859
884
|
}
|
|
860
885
|
var TEMPLATE_EXTENSION = { csv: "csv", xlsx: "xlsx" };
|
|
@@ -1031,6 +1056,8 @@ function createImportRouter(options) {
|
|
|
1031
1056
|
sample: preview.sample,
|
|
1032
1057
|
suggestion: preview.suggestion,
|
|
1033
1058
|
rowCount: preview.rowCount,
|
|
1059
|
+
arrayBound: preview.arrayBound,
|
|
1060
|
+
...preview.delimiter ? { delimiter: preview.delimiter } : {},
|
|
1034
1061
|
engineVersion: CORE_VERSION
|
|
1035
1062
|
};
|
|
1036
1063
|
response.json(body);
|