@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/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-B6KIIBRw.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-B6KIIBRw.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-B6KIIBRw.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-B6KIIBRw.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) {
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 = 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,11 @@ 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 rows = format === "xlsx" ? xlsxRows(stream, limits) : csvRows(stream, limits, found);
602
621
  const iterator = rows[Symbol.asyncIterator]();
603
622
  const first = await iterator.next();
604
- const headers = first.done ? [] : first.value.map(sampleText);
623
+ const headers = first.done ? [] : first.value.map(core.cellText);
605
624
  if (headers.length > limits.maxColumns) {
606
625
  throw new ImportError(
607
626
  "SHEET_TOO_LARGE",
@@ -612,7 +631,8 @@ async function readSheet(options) {
612
631
  return {
613
632
  format,
614
633
  headers,
615
- rows: closing(guardRows(remaining, headers.length, limits), options.stream)
634
+ rows: closing(guardRows(remaining, headers.length, limits), options.stream),
635
+ ...found.delimiter ? { delimiter: found.delimiter } : {}
616
636
  };
617
637
  } catch (error) {
618
638
  destroySource(options.stream);
@@ -664,6 +684,7 @@ async function runImport(options) {
664
684
  filename: options.filename,
665
685
  limits
666
686
  });
687
+ const decimal = options.decimal ?? core.decimalMarkFor(sheet.delimiter);
667
688
  const errors = [];
668
689
  let errorCount = 0;
669
690
  let imported = 0;
@@ -679,6 +700,7 @@ async function runImport(options) {
679
700
  batch = [];
680
701
  const result = core.applyMapping(rows, plan, config, {
681
702
  lang: options.lang,
703
+ decimal,
682
704
  rules: options.rules,
683
705
  lookups: options.lookups,
684
706
  firstRowNumber: firstRow
@@ -741,19 +763,22 @@ async function previewSheet(options) {
741
763
  try {
742
764
  for await (const row of sheet.rows) {
743
765
  rowCount++;
744
- if (sample.length < limits.sampleRows) sample.push(row.map(sampleText));
766
+ if (sample.length < limits.sampleRows) sample.push(row.map(core.cellText));
745
767
  }
746
768
  } catch (error) {
747
769
  destroySource(options.stream);
748
770
  throw error;
749
771
  }
750
- const { columns } = core.deriveImportColumns(options.config, { lang: options.lang });
772
+ const arrayBound = core.arrayBoundFor(sheet.headers, options.config, null, options.lang);
773
+ const { columns } = core.deriveImportColumns(options.config, { lang: options.lang, maxArrayRows: arrayBound });
751
774
  return {
752
775
  headers: sheet.headers,
753
776
  sample,
754
777
  rowCount,
755
778
  suggestion: core.suggestMapping(sheet.headers, columns, options.config?.entity ?? ""),
756
- format: sheet.format
779
+ format: sheet.format,
780
+ ...sheet.delimiter ? { delimiter: sheet.delimiter } : {},
781
+ arrayBound
757
782
  };
758
783
  }
759
784
  var TEMPLATE_EXTENSION = { csv: "csv", xlsx: "xlsx" };
@@ -802,6 +827,10 @@ function sheetName(name) {
802
827
  return (cleaned || "Import").slice(0, 31);
803
828
  }
804
829
 
830
+ Object.defineProperty(exports, "sampleText", {
831
+ enumerable: true,
832
+ get: function () { return core.cellText; }
833
+ });
805
834
  exports.DEFAULT_LIMITS = DEFAULT_LIMITS;
806
835
  exports.ImportError = ImportError;
807
836
  exports.TEMPLATE_EXTENSION = TEMPLATE_EXTENSION;
@@ -817,7 +846,6 @@ exports.previewSheet = previewSheet;
817
846
  exports.readSheet = readSheet;
818
847
  exports.resolveLimits = resolveLimits;
819
848
  exports.runImport = runImport;
820
- exports.sampleText = sampleText;
821
849
  exports.toByteStream = toByteStream;
822
850
  exports.toErrorBody = toErrorBody;
823
851
  exports.writeTemplate = writeTemplate;