@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.
- package/README.md +14 -1
- package/express.d.mts +1 -1
- package/express.d.ts +1 -1
- package/express.js +49 -21
- package/express.js.map +1 -1
- package/express.mjs +50 -22
- package/express.mjs.map +1 -1
- package/index.d.mts +4 -22
- package/index.d.ts +4 -22
- package/index.js +52 -23
- package/index.js.map +1 -1
- package/index.mjs +50 -23
- package/index.mjs.map +1 -1
- package/package.json +2 -2
- package/{write-template-C2dCEp48.d.mts → write-template-CzKVVjgT.d.mts} +20 -3
- package/{write-template-C2dCEp48.d.ts → write-template-CzKVVjgT.d.ts} +20 -3
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, forced) {
|
|
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 = forced ?? 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,12 @@ 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 forced = /\.tsv$/i.test(options.filename ?? "") ? " " : void 0;
|
|
720
|
+
const rows = format === "xlsx" ? xlsxRows(stream, limits) : csvRows(stream, limits, found, forced);
|
|
701
721
|
const iterator = rows[Symbol.asyncIterator]();
|
|
702
722
|
const first = await iterator.next();
|
|
703
|
-
const headers = first.done ? [] : first.value.map(
|
|
723
|
+
const headers = first.done ? [] : first.value.map(cellText);
|
|
704
724
|
if (headers.length > limits.maxColumns) {
|
|
705
725
|
throw new ImportError(
|
|
706
726
|
"SHEET_TOO_LARGE",
|
|
@@ -711,7 +731,8 @@ async function readSheet(options) {
|
|
|
711
731
|
return {
|
|
712
732
|
format,
|
|
713
733
|
headers,
|
|
714
|
-
rows: closing(guardRows(remaining, headers.length, limits), options.stream)
|
|
734
|
+
rows: closing(guardRows(remaining, headers.length, limits), options.stream),
|
|
735
|
+
...found.delimiter ? { delimiter: found.delimiter } : {}
|
|
715
736
|
};
|
|
716
737
|
} catch (error) {
|
|
717
738
|
destroySource(options.stream);
|
|
@@ -765,6 +786,7 @@ async function runImport(options) {
|
|
|
765
786
|
filename: options.filename,
|
|
766
787
|
limits
|
|
767
788
|
});
|
|
789
|
+
const decimal = options.decimal ?? decimalMarkFor(sheet.delimiter);
|
|
768
790
|
const errors = [];
|
|
769
791
|
let errorCount = 0;
|
|
770
792
|
let imported = 0;
|
|
@@ -780,6 +802,7 @@ async function runImport(options) {
|
|
|
780
802
|
batch = [];
|
|
781
803
|
const result = applyMapping(rows, plan, config, {
|
|
782
804
|
lang: options.lang,
|
|
805
|
+
decimal,
|
|
783
806
|
rules: options.rules,
|
|
784
807
|
lookups: options.lookups,
|
|
785
808
|
firstRowNumber: firstRow
|
|
@@ -842,19 +865,22 @@ async function previewSheet(options) {
|
|
|
842
865
|
try {
|
|
843
866
|
for await (const row of sheet.rows) {
|
|
844
867
|
rowCount++;
|
|
845
|
-
if (sample.length < limits.sampleRows) sample.push(row.map(
|
|
868
|
+
if (sample.length < limits.sampleRows) sample.push(row.map(cellText));
|
|
846
869
|
}
|
|
847
870
|
} catch (error) {
|
|
848
871
|
destroySource(options.stream);
|
|
849
872
|
throw error;
|
|
850
873
|
}
|
|
851
|
-
const
|
|
874
|
+
const arrayBound = arrayBoundFor(sheet.headers, options.config, null, options.lang);
|
|
875
|
+
const { columns } = deriveImportColumns(options.config, { lang: options.lang, maxArrayRows: arrayBound });
|
|
852
876
|
return {
|
|
853
877
|
headers: sheet.headers,
|
|
854
878
|
sample,
|
|
855
879
|
rowCount,
|
|
856
880
|
suggestion: suggestMapping(sheet.headers, columns, options.config?.entity ?? ""),
|
|
857
|
-
format: sheet.format
|
|
881
|
+
format: sheet.format,
|
|
882
|
+
...sheet.delimiter ? { delimiter: sheet.delimiter } : {},
|
|
883
|
+
arrayBound
|
|
858
884
|
};
|
|
859
885
|
}
|
|
860
886
|
var TEMPLATE_EXTENSION = { csv: "csv", xlsx: "xlsx" };
|
|
@@ -1031,6 +1057,8 @@ function createImportRouter(options) {
|
|
|
1031
1057
|
sample: preview.sample,
|
|
1032
1058
|
suggestion: preview.suggestion,
|
|
1033
1059
|
rowCount: preview.rowCount,
|
|
1060
|
+
arrayBound: preview.arrayBound,
|
|
1061
|
+
...preview.delimiter ? { delimiter: preview.delimiter } : {},
|
|
1034
1062
|
engineVersion: CORE_VERSION
|
|
1035
1063
|
};
|
|
1036
1064
|
response.json(body);
|