@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/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { I as ImportLimits } from './write-template-C2dCEp48.mjs';
2
- export { B as BatchInfo, a as ByteSource, D as DEFAULT_LIMITS, b as ImportRunResult, O as OnBatch, P as PreviewSheetOptions, R as ReadSheetOptions, c as RunImportOptions, S as SheetFormat, d as SheetPreview, e as SheetSource, f as TEMPLATE_EXTENSION, g as TEMPLATE_MEDIA_TYPE, T as TemplateFormat, W as WriteTemplateOptions, h as destroySource, i as detectFormat, j as guardRows, l as limitBytes, p as peek, k as previewSheet, r as readSheet, m as resolveLimits, n as runImport, t as toByteStream, w as writeTemplate } from './write-template-C2dCEp48.mjs';
3
- import '@dynamic-entity/core';
1
+ import { I as ImportLimits } from './write-template-CzKVVjgT.mjs';
2
+ export { B as BatchInfo, a as ByteSource, D as DEFAULT_LIMITS, b as ImportRunResult, O as OnBatch, P as PreviewSheetOptions, R as ReadSheetOptions, c as RunImportOptions, S as SheetFormat, d as SheetPreview, e as SheetSource, f as TEMPLATE_EXTENSION, g as TEMPLATE_MEDIA_TYPE, T as TemplateFormat, W as WriteTemplateOptions, h as destroySource, i as detectFormat, j as guardRows, l as limitBytes, p as peek, k as previewSheet, r as readSheet, m as resolveLimits, n as runImport, t as toByteStream, w as writeTemplate } from './write-template-CzKVVjgT.mjs';
3
+ export { cellText as sampleText } from '@dynamic-entity/core';
4
4
  import 'node:stream';
5
5
 
6
6
  /**
@@ -79,24 +79,6 @@ declare function toErrorBody(error: unknown): {
79
79
  body: ImportErrorBody;
80
80
  };
81
81
 
82
- /**
83
- * cell-text.ts — how a typed cell is rendered when text is what has to cross the wire.
84
- *
85
- * Its own module because both the reader and the preview need it, and a second copy would be
86
- * a second place for the date rule to be wrong.
87
- */
88
- /**
89
- * Render a cell as the text a mapping screen shows.
90
- *
91
- * It mirrors `coerceTypedCell`'s date rule on purpose. A preview sample crosses the wire as
92
- * text and the browser then runs `coerceCell` over that text, so if this rendered a `Date`
93
- * with `String()` the preview would show — and the browser would coerce — a different day
94
- * from the one the import will store. That is a preview lying about the only thing it exists
95
- * to show, and it would be wrong by exactly one day, silently, only for users at a negative
96
- * UTC offset. `run-import.spec.ts` asserts the two agree rather than trusting this comment.
97
- */
98
- declare function sampleText(raw: unknown): string;
99
-
100
82
  /**
101
83
  * guard-zip.ts — open the archive ourselves, and hand the parser only what it should see.
102
84
  *
@@ -177,4 +159,4 @@ declare function cellValue(raw: unknown): unknown;
177
159
  */
178
160
  declare function xlsxRows(source: AsyncIterable<Uint8Array>, limits: ImportLimits): AsyncGenerator<unknown[]>;
179
161
 
180
- export { ImportError, type ImportErrorBody, type ImportErrorCode, ImportLimits, cellValue, guardZip, sampleText, toErrorBody, xlsxRows };
162
+ export { ImportError, type ImportErrorBody, type ImportErrorCode, ImportLimits, cellValue, guardZip, toErrorBody, xlsxRows };
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { I as ImportLimits } from './write-template-C2dCEp48.js';
2
- export { B as BatchInfo, a as ByteSource, D as DEFAULT_LIMITS, b as ImportRunResult, O as OnBatch, P as PreviewSheetOptions, R as ReadSheetOptions, c as RunImportOptions, S as SheetFormat, d as SheetPreview, e as SheetSource, f as TEMPLATE_EXTENSION, g as TEMPLATE_MEDIA_TYPE, T as TemplateFormat, W as WriteTemplateOptions, h as destroySource, i as detectFormat, j as guardRows, l as limitBytes, p as peek, k as previewSheet, r as readSheet, m as resolveLimits, n as runImport, t as toByteStream, w as writeTemplate } from './write-template-C2dCEp48.js';
3
- import '@dynamic-entity/core';
1
+ import { I as ImportLimits } from './write-template-CzKVVjgT.js';
2
+ export { B as BatchInfo, a as ByteSource, D as DEFAULT_LIMITS, b as ImportRunResult, O as OnBatch, P as PreviewSheetOptions, R as ReadSheetOptions, c as RunImportOptions, S as SheetFormat, d as SheetPreview, e as SheetSource, f as TEMPLATE_EXTENSION, g as TEMPLATE_MEDIA_TYPE, T as TemplateFormat, W as WriteTemplateOptions, h as destroySource, i as detectFormat, j as guardRows, l as limitBytes, p as peek, k as previewSheet, r as readSheet, m as resolveLimits, n as runImport, t as toByteStream, w as writeTemplate } from './write-template-CzKVVjgT.js';
3
+ export { cellText as sampleText } from '@dynamic-entity/core';
4
4
  import 'node:stream';
5
5
 
6
6
  /**
@@ -79,24 +79,6 @@ declare function toErrorBody(error: unknown): {
79
79
  body: ImportErrorBody;
80
80
  };
81
81
 
82
- /**
83
- * cell-text.ts — how a typed cell is rendered when text is what has to cross the wire.
84
- *
85
- * Its own module because both the reader and the preview need it, and a second copy would be
86
- * a second place for the date rule to be wrong.
87
- */
88
- /**
89
- * Render a cell as the text a mapping screen shows.
90
- *
91
- * It mirrors `coerceTypedCell`'s date rule on purpose. A preview sample crosses the wire as
92
- * text and the browser then runs `coerceCell` over that text, so if this rendered a `Date`
93
- * with `String()` the preview would show — and the browser would coerce — a different day
94
- * from the one the import will store. That is a preview lying about the only thing it exists
95
- * to show, and it would be wrong by exactly one day, silently, only for users at a negative
96
- * UTC offset. `run-import.spec.ts` asserts the two agree rather than trusting this comment.
97
- */
98
- declare function sampleText(raw: unknown): string;
99
-
100
82
  /**
101
83
  * guard-zip.ts — open the archive ourselves, and hand the parser only what it should see.
102
84
  *
@@ -177,4 +159,4 @@ declare function cellValue(raw: unknown): unknown;
177
159
  */
178
160
  declare function xlsxRows(source: AsyncIterable<Uint8Array>, limits: ImportLimits): AsyncGenerator<unknown[]>;
179
161
 
180
- export { ImportError, type ImportErrorBody, type ImportErrorCode, ImportLimits, cellValue, guardZip, sampleText, toErrorBody, xlsxRows };
162
+ export { ImportError, type ImportErrorBody, type ImportErrorCode, ImportLimits, cellValue, guardZip, toErrorBody, xlsxRows };
package/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
+ var core = require('@dynamic-entity/core');
3
4
  var zlib = require('zlib');
4
5
  var ExcelJS = require('exceljs');
5
6
  var stream = require('stream');
6
- var core = require('@dynamic-entity/core');
7
7
 
8
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
9
 
@@ -140,17 +140,6 @@ function destroySource(source) {
140
140
  } catch {
141
141
  }
142
142
  }
143
-
144
- // src/cell-text.ts
145
- function sampleText(raw) {
146
- if (raw === null || raw === void 0) return "";
147
- if (raw instanceof Date) {
148
- if (Number.isNaN(raw.getTime())) return "";
149
- const iso = raw.toISOString();
150
- return raw.getTime() % 864e5 === 0 ? iso.slice(0, 10) : iso;
151
- }
152
- return typeof raw === "string" ? raw : String(raw);
153
- }
154
143
  var LOCAL_HEADER = 67324752;
155
144
  var CENTRAL_HEADER = 33639248;
156
145
  var END_OF_CENTRAL = 101010256;
@@ -467,6 +456,12 @@ async function guardZip(source, limits) {
467
456
  }
468
457
  }
469
458
  if (!sheet) {
459
+ if (!kept.has("xl/workbook.xml")) {
460
+ throw new ImportError(
461
+ "UNSUPPORTED_FORMAT",
462
+ "This is a zip archive that is not an Excel workbook. Upload an .xlsx or .csv file."
463
+ );
464
+ }
470
465
  throw new ImportError("MALFORMED_FILE", "This workbook has no worksheet.");
471
466
  }
472
467
  const ordered = [...WANTED, sheet.name].map((wanted) => kept.get(wanted)).filter((entry) => entry !== void 0);
@@ -557,13 +552,36 @@ function detectFormat(head) {
557
552
  }
558
553
  return "csv";
559
554
  }
560
- async function* csvRows(stream) {
555
+ async function* csvRows(stream, limits, found, forced) {
561
556
  const decoder = new TextDecoder("utf-8");
562
- const reader = core.createCsvReader();
557
+ const maxHeader = limits.maxCellLength * limits.maxColumns + limits.maxColumns;
558
+ let held = "";
559
+ let reader = null;
560
+ const open = (headerLine) => {
561
+ found.delimiter = forced ?? core.detectDelimiter(headerLine);
562
+ return core.createCsvReader({ delimiter: found.delimiter });
563
+ };
563
564
  for await (const chunk of stream) {
564
- for (const row of reader.push(decoder.decode(chunk, { stream: true }))) yield row;
565
+ const text = decoder.decode(chunk, { stream: true });
566
+ if (!reader) {
567
+ held += text;
568
+ const line = core.completeFirstLine(held);
569
+ if (line === null) {
570
+ if (held.length > maxHeader) {
571
+ throw new ImportError("SHEET_TOO_LARGE", "The header row is longer than any row may be.");
572
+ }
573
+ continue;
574
+ }
575
+ reader = open(line);
576
+ for (const row of reader.push(held)) yield row;
577
+ held = "";
578
+ continue;
579
+ }
580
+ for (const row of reader.push(text)) yield row;
565
581
  }
566
- for (const row of reader.push(decoder.decode())) yield row;
582
+ const rest = held + decoder.decode();
583
+ reader ??= open(core.completeFirstLine(rest) ?? rest);
584
+ for (const row of reader.push(rest)) yield row;
567
585
  for (const row of reader.end()) yield row;
568
586
  }
569
587
  async function* guardRows(rows, width, limits) {
@@ -598,10 +616,12 @@ async function readSheet(options) {
598
616
  const counted = limitBytes(toByteStream(options.stream), limits.maxBytes);
599
617
  const { head, stream } = await peek(counted, MAGIC_BYTES);
600
618
  const format = detectFormat(head);
601
- const rows = format === "xlsx" ? xlsxRows(stream, limits) : csvRows(stream);
619
+ const found = {};
620
+ const forced = /\.tsv$/i.test(options.filename ?? "") ? " " : void 0;
621
+ const rows = format === "xlsx" ? xlsxRows(stream, limits) : csvRows(stream, limits, found, forced);
602
622
  const iterator = rows[Symbol.asyncIterator]();
603
623
  const first = await iterator.next();
604
- const headers = first.done ? [] : first.value.map(sampleText);
624
+ const headers = first.done ? [] : first.value.map(core.cellText);
605
625
  if (headers.length > limits.maxColumns) {
606
626
  throw new ImportError(
607
627
  "SHEET_TOO_LARGE",
@@ -612,7 +632,8 @@ async function readSheet(options) {
612
632
  return {
613
633
  format,
614
634
  headers,
615
- rows: closing(guardRows(remaining, headers.length, limits), options.stream)
635
+ rows: closing(guardRows(remaining, headers.length, limits), options.stream),
636
+ ...found.delimiter ? { delimiter: found.delimiter } : {}
616
637
  };
617
638
  } catch (error) {
618
639
  destroySource(options.stream);
@@ -664,6 +685,7 @@ async function runImport(options) {
664
685
  filename: options.filename,
665
686
  limits
666
687
  });
688
+ const decimal = options.decimal ?? core.decimalMarkFor(sheet.delimiter);
667
689
  const errors = [];
668
690
  let errorCount = 0;
669
691
  let imported = 0;
@@ -679,6 +701,7 @@ async function runImport(options) {
679
701
  batch = [];
680
702
  const result = core.applyMapping(rows, plan, config, {
681
703
  lang: options.lang,
704
+ decimal,
682
705
  rules: options.rules,
683
706
  lookups: options.lookups,
684
707
  firstRowNumber: firstRow
@@ -741,19 +764,22 @@ async function previewSheet(options) {
741
764
  try {
742
765
  for await (const row of sheet.rows) {
743
766
  rowCount++;
744
- if (sample.length < limits.sampleRows) sample.push(row.map(sampleText));
767
+ if (sample.length < limits.sampleRows) sample.push(row.map(core.cellText));
745
768
  }
746
769
  } catch (error) {
747
770
  destroySource(options.stream);
748
771
  throw error;
749
772
  }
750
- const { columns } = core.deriveImportColumns(options.config, { lang: options.lang });
773
+ const arrayBound = core.arrayBoundFor(sheet.headers, options.config, null, options.lang);
774
+ const { columns } = core.deriveImportColumns(options.config, { lang: options.lang, maxArrayRows: arrayBound });
751
775
  return {
752
776
  headers: sheet.headers,
753
777
  sample,
754
778
  rowCount,
755
779
  suggestion: core.suggestMapping(sheet.headers, columns, options.config?.entity ?? ""),
756
- format: sheet.format
780
+ format: sheet.format,
781
+ ...sheet.delimiter ? { delimiter: sheet.delimiter } : {},
782
+ arrayBound
757
783
  };
758
784
  }
759
785
  var TEMPLATE_EXTENSION = { csv: "csv", xlsx: "xlsx" };
@@ -802,6 +828,10 @@ function sheetName(name) {
802
828
  return (cleaned || "Import").slice(0, 31);
803
829
  }
804
830
 
831
+ Object.defineProperty(exports, "sampleText", {
832
+ enumerable: true,
833
+ get: function () { return core.cellText; }
834
+ });
805
835
  exports.DEFAULT_LIMITS = DEFAULT_LIMITS;
806
836
  exports.ImportError = ImportError;
807
837
  exports.TEMPLATE_EXTENSION = TEMPLATE_EXTENSION;
@@ -817,7 +847,6 @@ exports.previewSheet = previewSheet;
817
847
  exports.readSheet = readSheet;
818
848
  exports.resolveLimits = resolveLimits;
819
849
  exports.runImport = runImport;
820
- exports.sampleText = sampleText;
821
850
  exports.toByteStream = toByteStream;
822
851
  exports.toErrorBody = toErrorBody;
823
852
  exports.writeTemplate = writeTemplate;