@dynamic-entity/server 1.14.0 → 2.0.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.
Files changed (2) hide show
  1. package/README.md +39 -1
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -129,6 +129,16 @@ console.log(`imported ${result.imported}, skipped ${result.skipped}`);
129
129
  **awaits** `onBatch`, and drops the batch. Peak memory is a function of `batchSize`, not of the
130
130
  file's size — which is the entire reason this package exists.
131
131
 
132
+ **One qualification, for xlsx.** That statement is exact for CSV, which is read as a byte
133
+ stream from first row to last. An `.xlsx` file is a zip archive, and exceljs cannot be handed
134
+ a stream of one: `guardZip` validates the archive and rebuilds it in memory before exceljs is
135
+ constructed, so the *compressed* file is resident for the length of the import. It is bounded
136
+ by `maxBytes` rather than by `batchSize`, and it is the compressed size — a fifty-megabyte
137
+ workbook is fifty megabytes here, not the several hundred its rows would occupy expanded. The
138
+ row-by-row half above still holds: what scales with `batchSize` is everything downstream of
139
+ the reader. `xlsx-source.ts` has always said this in its own header; it belongs where a
140
+ reader meets the claim first.
141
+
132
142
  ---
133
143
 
134
144
  ## Four things to know before you deploy this
@@ -203,7 +213,35 @@ catastrophic backtrack. See [SECURITY.md](../../SECURITY.md).
203
213
 
204
214
  ## What it holds in memory
205
215
 
206
- Stated rather than left to be discovered:
216
+ ```mermaid
217
+ flowchart LR
218
+ U["the browser<br>uploads a file"]
219
+ R["readSheet()<br>streamed, limits enforced"]
220
+ M["applyMapping()<br>one batch of 500"]
221
+ Y["your onImport<br>awaited"]
222
+ C["imported: 50000<br>records: empty"]
223
+
224
+ U -->|"multipart POST"| R
225
+ R --> M
226
+ M -->|"records"| Y
227
+ Y -.->|"resolves: drop the batch, pull the next 500"| R
228
+ R -.->|"when the file ends"| C
229
+
230
+ classDef yours stroke-width:3px,stroke-dasharray:5 3
231
+ class Y yours
232
+ ```
233
+
234
+ The dashed edge back to `readSheet` is the whole package. A batch is mapped, handed to your
235
+ writer, **awaited**, and dropped before the next is pulled — so peak memory downstream of the
236
+ reader is a function of `batchSize` and not of the file. Not awaiting would turn bounded memory
237
+ into an unbounded queue of pending writes, which is the same failure arrived at from the other
238
+ side. For xlsx, add the rebuilt archive `guardZip` holds — see the qualification above.
239
+
240
+ Measured on a fifty-thousand-row, thirty-five-column sheet: **2.7 MB** of collected heap, against
241
+ 163 MB with the per-batch flush removed. `stress.spec.ts` asserts it at 16 MB, chosen to
242
+ discriminate rather than to be comfortable.
243
+
244
+ The rest, stated rather than left to be discovered:
207
245
 
208
246
  - **CSV streams properly.** Rows are read incrementally and never accumulated.
209
247
  - **Exactly one worksheet is read** — the lowest-numbered `sheetN.xml`, not whichever the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-entity/server",
3
- "version": "1.14.0",
3
+ "version": "2.0.1",
4
4
  "description": "Streaming spreadsheet import for @dynamic-entity/core, with an Express adapter.",
5
5
  "keywords": [
6
6
  "dynamic-entity",
@@ -47,7 +47,7 @@
47
47
  "exceljs": "4.4.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@dynamic-entity/core": "^1.14.0",
50
+ "@dynamic-entity/core": "^2.0.1",
51
51
  "express": "^4.18.0 || ^5.0.0"
52
52
  },
53
53
  "peerDependenciesMeta": {