beast-agent 2.6.2 → 2.6.4

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.
@@ -0,0 +1,474 @@
1
+ 'use strict';
2
+
3
+ /* Sıfır bağımlılıklı .xlsx okuma/yazma — zip (store/deflate) + minimal OOXML.
4
+ Okuma: EOCD → central directory → zlib.inflateRawSync → workbook/rels/
5
+ sharedStrings/sheet XML ayrıştırma. Formüller önbellek değerleriyle okunur;
6
+ yaygın tarih numFmt'leri tanınır ve ISO string'e çevrilir.
7
+ Yazma: geçerli minimal paket üretir (inlineStr — sharedStrings yazmaz).
8
+ Excel/Sheets/LibreOffice açar, sayfa kodlaması UTF-8.
9
+ Not: zip64 yalnız temel seviyede desteklenir (xlsx'ler küçüktür). */
10
+
11
+ const fs = require('fs');
12
+ const zlib = require('zlib');
13
+
14
+ /* ---------- zip okuma ---------- */
15
+
16
+ function zipRead(buf) {
17
+ let eocd = -1;
18
+ const min = Math.max(0, buf.length - 66000);
19
+ for (let i = buf.length - 22; i >= min; i--) {
20
+ if (buf.readUInt32LE(i) === 0x06054b50) {
21
+ eocd = i;
22
+ break;
23
+ }
24
+ }
25
+ if (eocd < 0) throw new Error('xlsx: zip EOCD bulunamadı — dosya bozuk ya da eski .xls biçimi');
26
+
27
+ let count = buf.readUInt16LE(eocd + 10);
28
+ let cdOff = buf.readUInt32LE(eocd + 16);
29
+
30
+ /* zip64: EOCD locator (eocd-20) → zip64 EOCD'den gerçek değerleri al */
31
+ if (eocd >= 20 && buf.readUInt32LE(eocd - 20) === 0x07064b50) {
32
+ const z64 = buf.readBigUInt64LE(eocd - 20 + 8);
33
+ if (buf.readUInt32LE(Number(z64)) === 0x06064b50) {
34
+ count = Number(buf.readBigUInt64LE(Number(z64) + 32));
35
+ cdOff = Number(buf.readBigUInt64LE(Number(z64) + 48));
36
+ }
37
+ }
38
+
39
+ const files = new Map();
40
+ let p = cdOff;
41
+ for (let n = 0; n < count; n++) {
42
+ if (p + 46 > buf.length || buf.readUInt32LE(p) !== 0x02014b50) break;
43
+ const method = buf.readUInt16LE(p + 10);
44
+ const csize = buf.readUInt32LE(p + 20);
45
+ const nameLen = buf.readUInt16LE(p + 28);
46
+ const extraLen = buf.readUInt16LE(p + 30);
47
+ const commLen = buf.readUInt16LE(p + 32);
48
+ const lho = buf.readUInt32LE(p + 42);
49
+ const name = buf.slice(p + 46, p + 46 + nameLen).toString('utf8');
50
+ p += 46 + nameLen + extraLen + commLen;
51
+ if (name.endsWith('/')) continue; /* dizin girişi */
52
+ if (lho + 30 > buf.length) continue;
53
+ const lNameLen = buf.readUInt16LE(lho + 26);
54
+ const lExtraLen = buf.readUInt16LE(lho + 28);
55
+ const dataStart = lho + 30 + lNameLen + lExtraLen;
56
+ const raw = buf.slice(dataStart, dataStart + csize);
57
+ files.set(name, method === 0 ? Buffer.from(raw) : zlib.inflateRawSync(raw));
58
+ }
59
+ if (!files.size) throw new Error('xlsx: zip girişi okunamadı');
60
+ return files;
61
+ }
62
+
63
+ /* ---------- zip yazma ---------- */
64
+
65
+ const CRC_TABLE = (() => {
66
+ const t = new Uint32Array(256);
67
+ for (let n = 0; n < 256; n++) {
68
+ let c = n;
69
+ for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
70
+ t[n] = c;
71
+ }
72
+ return t;
73
+ })();
74
+
75
+ function crc32(b) {
76
+ let c = ~0;
77
+ for (let i = 0; i < b.length; i++) c = CRC_TABLE[(c ^ b[i]) & 0xff] ^ (c >>> 8);
78
+ return ~c >>> 0;
79
+ }
80
+
81
+ function zipWrite(entries) {
82
+ const parts = [];
83
+ const central = [];
84
+ let offset = 0;
85
+ const now = new Date();
86
+ const dosTime = ((now.getHours() & 31) << 11) | ((now.getMinutes() & 63) << 5) | ((now.getSeconds() / 2) & 31);
87
+ const dosDate = (((now.getFullYear() - 1980) & 127) << 9) | (((now.getMonth() + 1) & 15) << 5) | (now.getDate() & 31);
88
+ for (const e of entries) {
89
+ const nameBuf = Buffer.from(e.name, 'utf8');
90
+ const deflated = zlib.deflateRawSync(e.data);
91
+ const method = deflated.length < e.data.length ? 8 : 0;
92
+ const data = method === 8 ? deflated : e.data;
93
+ const crc = crc32(e.data);
94
+
95
+ const lh = Buffer.alloc(30);
96
+ lh.writeUInt32LE(0x04034b50, 0);
97
+ lh.writeUInt16LE(20, 4);
98
+ lh.writeUInt16LE(0, 6);
99
+ lh.writeUInt16LE(method, 8);
100
+ lh.writeUInt16LE(dosTime, 10);
101
+ lh.writeUInt16LE(dosDate, 12);
102
+ lh.writeUInt32LE(crc, 14);
103
+ lh.writeUInt32LE(data.length, 18);
104
+ lh.writeUInt32LE(e.data.length, 22);
105
+ lh.writeUInt16LE(nameBuf.length, 26);
106
+ lh.writeUInt16LE(0, 28);
107
+ parts.push(lh, nameBuf, data);
108
+
109
+ const ch = Buffer.alloc(46);
110
+ ch.writeUInt32LE(0x02014b50, 0);
111
+ ch.writeUInt16LE(20, 4);
112
+ ch.writeUInt16LE(20, 6);
113
+ ch.writeUInt16LE(0, 8);
114
+ ch.writeUInt16LE(method, 10);
115
+ ch.writeUInt16LE(dosTime, 12);
116
+ ch.writeUInt16LE(dosDate, 14);
117
+ ch.writeUInt32LE(crc, 16);
118
+ ch.writeUInt32LE(data.length, 20);
119
+ ch.writeUInt32LE(e.data.length, 24);
120
+ ch.writeUInt16LE(nameBuf.length, 28);
121
+ ch.writeUInt32LE(offset, 42);
122
+ central.push(Buffer.concat([ch, nameBuf]));
123
+
124
+ offset += 30 + nameBuf.length + data.length;
125
+ }
126
+ const cd = Buffer.concat(central);
127
+ const eocd = Buffer.alloc(22);
128
+ eocd.writeUInt32LE(0x06054b50, 0);
129
+ eocd.writeUInt16LE(entries.length, 8);
130
+ eocd.writeUInt16LE(entries.length, 10);
131
+ eocd.writeUInt32LE(cd.length, 12);
132
+ eocd.writeUInt32LE(offset, 16);
133
+ return Buffer.concat([...parts, cd, eocd]);
134
+ }
135
+
136
+ /* ---------- xml yardımcıları ---------- */
137
+
138
+ const ENT = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'", '#39': "'" };
139
+
140
+ function unescXml(s) {
141
+ return String(s)
142
+ .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16)))
143
+ .replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(parseInt(d, 10)))
144
+ .replace(/&(amp|lt|gt|quot|apos|#39);/g, (_, e) => ENT[e]);
145
+ }
146
+
147
+ function escText(s) {
148
+ return String(s)
149
+ .replace(/&/g, '&amp;')
150
+ .replace(/</g, '&lt;')
151
+ .replace(/>/g, '&gt;')
152
+ .replace(/\r/g, '&#xD;')
153
+ .replace(/\n/g, '&#xA;')
154
+ .replace(/\t/g, '&#x9;');
155
+ }
156
+
157
+ function escAttr(s) {
158
+ return escText(s).replace(/"/g, '&quot;');
159
+ }
160
+
161
+ function colToName(n) {
162
+ let s = '';
163
+ while (n > 0) {
164
+ const r = (n - 1) % 26;
165
+ s = String.fromCharCode(65 + r) + s;
166
+ n = Math.floor((n - 1) / 26);
167
+ }
168
+ return s;
169
+ }
170
+
171
+ function refToCell(ref) {
172
+ const m = /^([A-Z]+)(\d+)$/i.exec(String(ref || '').trim());
173
+ if (!m) return null;
174
+ let col = 0;
175
+ for (const ch of m[1].toUpperCase()) col = col * 26 + (ch.charCodeAt(0) - 64);
176
+ return { row: parseInt(m[2], 10), col };
177
+ }
178
+
179
+ /* ---------- tarih: excel seri no → ISO string ---------- */
180
+
181
+ const DATE_BUILTIN = new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 45, 46, 47, 50, 51, 52, 53, 54, 55, 56, 57, 58]);
182
+
183
+ function serialToString(n) {
184
+ const days = Math.floor(n);
185
+ const frac = n - days;
186
+ /* 1900 sistemi: 1899-12-30 = seri 0 referansı; 60 = hayalet 1900-02-29 */
187
+ if (days === 60) return '1900-02-29';
188
+ const base = days >= 61 ? 25569 : 25568; /* <61'de hayalet gün kayması telafisi */
189
+ const ms = (days - base) * 86400000 + Math.round(frac * 86400000);
190
+ const d = new Date(ms);
191
+ if (isNaN(d.getTime())) return String(n);
192
+ const p2 = (x) => String(x).padStart(2, '0');
193
+ const date = d.getUTCFullYear() + '-' + p2(d.getUTCMonth() + 1) + '-' + p2(d.getUTCDate());
194
+ if (frac < 1e-6 && frac > -1e-6) return date;
195
+ return date + ' ' + p2(d.getUTCHours()) + ':' + p2(d.getUTCMinutes()) + ':' + p2(d.getUTCSeconds());
196
+ }
197
+
198
+ function dateToDateSerial(d) {
199
+ const t = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds());
200
+ let days = Math.round(t / 86400000) + 25569;
201
+ /* 1900-03-01 öncesi hayalet gün kayması — pratikte nadir, yine de doğru yaz */
202
+ const y = d.getUTCFullYear();
203
+ if (y < 1900 || (y === 1900 && (d.getUTCMonth() < 2 || (d.getUTCMonth() === 2 && d.getUTCDate() < 1)))) days += 1;
204
+ return days + (t % 86400000) / 86400000;
205
+ }
206
+
207
+ function parseStyles(files) {
208
+ const xml = files.get('xl/styles.xml');
209
+ const dateStyles = new Set();
210
+ if (!xml) return dateStyles;
211
+ const s = xml.toString('utf8');
212
+ const custom = new Map();
213
+ for (const m of s.matchAll(/<numFmt\b[^>]*numFmtId="(\d+)"[^>]*formatCode="([^"]*)"[^>]*\/?>/g)) {
214
+ custom.set(Number(m[1]), unescXml(m[2]));
215
+ }
216
+ const isDateFmt = (id) => DATE_BUILTIN.has(id) || (custom.has(id) && /[yYmMdDhHsS]/.test(custom.get(id).replace(/"[^"]*"|\\./g, '')));
217
+ const cx = /<cellXfs[^>]*>([\s\S]*?)<\/cellXfs>/.exec(s);
218
+ if (cx) {
219
+ let idx = 0;
220
+ for (const m of cx[1].matchAll(/<xf\b[^>]*?(?:\/>|>)/g)) {
221
+ const nf = /numFmtId="(\d+)"/.exec(m[0]);
222
+ if (nf && isDateFmt(Number(nf[1]))) dateStyles.add(idx);
223
+ idx++;
224
+ }
225
+ }
226
+ return dateStyles;
227
+ }
228
+
229
+ function parseSharedStrings(files) {
230
+ const xml = files.get('xl/sharedStrings.xml');
231
+ if (!xml) return [];
232
+ const out = [];
233
+ const s = xml.toString('utf8');
234
+ for (const m of s.matchAll(/<si(?:\s[^>]*)?>([\s\S]*?)<\/si>/g)) {
235
+ let text = '';
236
+ for (const t of m[1].matchAll(/<t(?:\s[^>]*)?>([\s\S]*?)<\/t>/g)) text += unescXml(t[1]);
237
+ if (!text && !/<t[\s>]/.test(m[1])) text = unescXml(m[1].replace(/<[^>]+>/g, ''));
238
+ out.push(text);
239
+ }
240
+ return out;
241
+ }
242
+
243
+ function parseWorkbook(files) {
244
+ const wb = files.get('xl/workbook.xml');
245
+ if (!wb) throw new Error('xlsx: workbook.xml yok — geçerli bir xlsx değil');
246
+ const rels = files.get('xl/_rels/workbook.xml.rels');
247
+ const relMap = new Map();
248
+ if (rels) {
249
+ for (const m of rels.toString('utf8').matchAll(/<Relationship\b[^>]*>/g)) {
250
+ const tag = m[0];
251
+ const id = /Id="([^"]*)"/.exec(tag);
252
+ const target = /Target="([^"]*)"/.exec(tag);
253
+ const ext = /TargetMode="External"/.test(tag);
254
+ if (id && target && !ext) {
255
+ let t = target[1];
256
+ if (t.startsWith('/')) t = t.slice(1);
257
+ else if (!/^(xl\/|\/)/.test(t)) t = 'xl/' + t;
258
+ relMap.set(id[1], t);
259
+ }
260
+ }
261
+ }
262
+ const sheets = [];
263
+ for (const m of wb.toString('utf8').matchAll(/<sheet\b[^>]*>/g)) {
264
+ const tag = m[0];
265
+ const name = /name="([^"]*)"/.exec(tag);
266
+ const rid = /r:id="([^"]*)"/.exec(tag);
267
+ const sid = /sheetId="([^"]*)"/.exec(tag);
268
+ const target = rid && relMap.get(rid[1]);
269
+ if (name && target && files.has(target)) {
270
+ sheets.push({ name: unescXml(name[1]), path: target, ...(sid ? { sheetId: Number(sid[1]) } : {}) });
271
+ }
272
+ }
273
+ if (!sheets.length) throw new Error('xlsx: çalışma sayfası bulunamadı');
274
+ return sheets;
275
+ }
276
+
277
+ function cellValue(attrs, inner, shared, dateStyles) {
278
+ const t = /t="([^"]*)"/.exec(attrs);
279
+ const st = /s="([^"]*)"/.exec(attrs);
280
+ const type = t ? t[1] : 'n';
281
+ if (type === 'inlineStr') {
282
+ let text = '';
283
+ for (const m of inner.matchAll(/<t(?:\s[^>]*)?>([\s\S]*?)<\/t>/g)) text += unescXml(m[1]);
284
+ return text;
285
+ }
286
+ const v = /<v(?:\s[^>]*)?>([\s\S]*?)<\/v>/.exec(inner);
287
+ const raw = v ? unescXml(v[1]) : '';
288
+ if (type === 's') return shared[Number(raw)] ?? '';
289
+ if (type === 'b') return raw === '1' || /^(true|1)$/i.test(raw);
290
+ if (type === 'e') return raw ? '#' + raw.replace(/^#/, '') : '#HATA';
291
+ if (type === 'str') return raw;
292
+ /* sayı (n) — tarih stiliyse ISO'ya çevir */
293
+ if (raw === '') return '';
294
+ const num = Number(raw);
295
+ if (!Number.isFinite(num)) return raw;
296
+ if (st && dateStyles.has(Number(st[1])) && num > 0) return serialToString(num);
297
+ return num;
298
+ }
299
+
300
+ function parseSheetXml(xml, shared, dateStyles) {
301
+ const rows = [];
302
+ const filled = new Map(); /* rowIndex → { col: value } */
303
+ let maxCol = 0;
304
+ for (const rm of xml.matchAll(/<row\b([^>]*)>([\s\S]*?)<\/row>|<row\b([^>]*)\/>/g)) {
305
+ const attrs = rm[1] || rm[3] || '';
306
+ const inner = rm[2] || '';
307
+ const rAttr = /r="(\d+)"/.exec(attrs);
308
+ const rowIdx = rAttr ? Number(rAttr[1]) : rows.length + 1;
309
+ const rowMap = new Map();
310
+ let posCol = 0;
311
+ for (const cm of inner.matchAll(/<c\b([^>]*?)(?:\/>|>([\s\S]*?)<\/c>)/g)) {
312
+ const cAttrs = cm[1] || '';
313
+ const cInner = cm[2] || '';
314
+ const ref = /r="([A-Z]+\d+)"/.exec(cAttrs);
315
+ const cell = ref ? refToCell(ref[1]) : null;
316
+ const colIdx = cell ? cell.col : posCol + 1;
317
+ posCol = colIdx;
318
+ const val = cellValue(cAttrs, cInner, shared, dateStyles);
319
+ const isEmpty = val === '' || val === null || val === undefined;
320
+ if (!isEmpty) {
321
+ rowMap.set(colIdx, val);
322
+ if (colIdx > maxCol) maxCol = colIdx;
323
+ }
324
+ }
325
+ if (rowMap.size) filled.set(rowIdx, rowMap);
326
+ }
327
+ if (!filled.size) return [];
328
+ const lastRow = Math.max(...filled.keys());
329
+ for (let r = 1; r <= lastRow; r++) {
330
+ const m = filled.get(r);
331
+ const row = [];
332
+ for (let c = 1; c <= maxCol; c++) row.push(m && m.has(c) ? m.get(c) : '');
333
+ rows.push(row);
334
+ }
335
+ /* sondaki boş satır/sütunları kırp */
336
+ while (rows.length && rows[rows.length - 1].every((v) => v === '')) rows.pop();
337
+ let cols = maxCol;
338
+ while (cols > 1 && rows.every((r) => r[cols - 1] === '')) cols--;
339
+ return rows.map((r) => r.slice(0, cols));
340
+ }
341
+
342
+ /* ---------- genel API ---------- */
343
+
344
+ /* read(bufOrPath) → [{ name, rows }] */
345
+ function read(src) {
346
+ const buf = Buffer.isBuffer(src) ? src : fs.readFileSync(src);
347
+ const files = zipRead(buf);
348
+ const shared = parseSharedStrings(files);
349
+ const dateStyles = parseStyles(files);
350
+ const sheets = parseWorkbook(files);
351
+ return sheets.map((sh) => ({ name: sh.name, rows: parseSheetXml(files.get(sh.path).toString('utf8'), shared, dateStyles) }));
352
+ }
353
+
354
+ /* ---------- yazma ---------- */
355
+
356
+ function sanitizeSheetName(name, i) {
357
+ let n = String(name == null ? '' : name).replace(/[\\/*?:[\]]/g, ' ').trim().slice(0, 31);
358
+ return n || 'Sheet' + (i + 1);
359
+ }
360
+
361
+ function cellXml(val, col, row) {
362
+ const ref = colToName(col) + row;
363
+ if (val === null || val === undefined || val === '') return '';
364
+ if (typeof val === 'number' && Number.isFinite(val)) return `<c r="${ref}"><v>${val}</v></c>`;
365
+ if (typeof val === 'boolean') return `<c r="${ref}" t="b"><v>${val ? 1 : 0}</v></c>`;
366
+ if (val instanceof Date && !isNaN(val.getTime())) {
367
+ return `<c r="${ref}" s="1"><v>${dateToDateSerial(val)}</v></c>`;
368
+ }
369
+ let s = String(val);
370
+ if (/^=/.test(s)) {
371
+ /* formül: önbelleksiz — Excel açınca hesaplar; t="str" değil f düğümü */
372
+ return `<c r="${ref}"><f>${escText(s.slice(1))}</f></c>`;
373
+ }
374
+ return `<c r="${ref}" t="inlineStr"><is><t xml:space="preserve">${escText(s)}</t></is></c>`;
375
+ }
376
+
377
+ function sheetXml(rows) {
378
+ const out = ['<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>'];
379
+ const nRows = Math.min(rows.length, 1048576);
380
+ for (let r = 0; r < nRows; r++) {
381
+ const row = rows[r] || [];
382
+ const nCols = Math.min(row.length, 16384);
383
+ let cells = '';
384
+ for (let c = 0; c < nCols; c++) cells += cellXml(row[c], c + 1, r + 1);
385
+ if (cells) out.push(`<row r="${r + 1}">${cells}</row>`);
386
+ }
387
+ out.push('</sheetData></worksheet>');
388
+ return out.join('');
389
+ }
390
+
391
+ /* rows: dizi-dizisi YA DA obje-dizisi (obje modunda anahtarlar başlık olur) */
392
+ function normalizeRows(rows) {
393
+ if (!Array.isArray(rows)) throw new Error('rows bir dizi olmalı');
394
+ const isObjects = rows.length && rows.every((r) => r && typeof r === 'object' && !Array.isArray(r) && !(r instanceof Date));
395
+ if (!isObjects) return rows.map((r) => (Array.isArray(r) ? r : [r]));
396
+ const keys = [];
397
+ for (const o of rows) for (const k of Object.keys(o)) if (!keys.includes(k)) keys.push(k);
398
+ return [keys, ...rows.map((o) => keys.map((k) => (o[k] == null ? '' : o[k])))];
399
+ }
400
+
401
+ /* write(sheets) → Buffer ; sheets: [{ name, rows }] */
402
+ function write(sheets) {
403
+ const list = (Array.isArray(sheets) ? sheets : [sheets]).map((s, i) => ({
404
+ name: sanitizeSheetName(s && s.name, i),
405
+ rows: normalizeRows(s && s.rows ? s.rows : []),
406
+ }));
407
+ if (!list.length) throw new Error('en az bir sayfa gerekli');
408
+
409
+ const entries = [];
410
+ const xmlDecl = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
411
+
412
+ entries.push({
413
+ name: '[Content_Types].xml',
414
+ data: Buffer.from(
415
+ xmlDecl +
416
+ '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
417
+ '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>' +
418
+ '<Default Extension="xml" ContentType="application/xml"/>' +
419
+ '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>' +
420
+ '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>' +
421
+ list.map((_, i) => `<Override PartName="/xl/worksheets/sheet${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`).join('') +
422
+ '</Types>'
423
+ ),
424
+ });
425
+ entries.push({
426
+ name: '_rels/.rels',
427
+ data: Buffer.from(
428
+ xmlDecl +
429
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
430
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' +
431
+ '</Relationships>'
432
+ ),
433
+ });
434
+ entries.push({
435
+ name: 'xl/workbook.xml',
436
+ data: Buffer.from(
437
+ xmlDecl +
438
+ '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets>' +
439
+ list.map((s, i) => `<sheet name="${escAttr(s.name)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`).join('') +
440
+ '</sheets></workbook>'
441
+ ),
442
+ });
443
+ entries.push({
444
+ name: 'xl/_rels/workbook.xml.rels',
445
+ data: Buffer.from(
446
+ xmlDecl +
447
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
448
+ list.map((_, i) => `<Relationship Id="rId${i + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${i + 1}.xml"/>`).join('') +
449
+ `<Relationship Id="rId${list.length + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>` +
450
+ '</Relationships>'
451
+ ),
452
+ });
453
+ entries.push({
454
+ name: 'xl/styles.xml',
455
+ data: Buffer.from(
456
+ xmlDecl +
457
+ '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">' +
458
+ '<fonts count="1"><font><sz val="11"/><name val="Calibri"/></font></fonts>' +
459
+ '<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>' +
460
+ '<borders count="1"><border/></borders>' +
461
+ '<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>' +
462
+ '<cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="14" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/></cellXfs>' +
463
+ '<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>' +
464
+ '</styleSheet>'
465
+ ),
466
+ });
467
+ list.forEach((s, i) => {
468
+ entries.push({ name: `xl/worksheets/sheet${i + 1}.xml`, data: Buffer.from(sheetXml(s.rows)) });
469
+ });
470
+
471
+ return zipWrite(entries);
472
+ }
473
+
474
+ module.exports = { read, write, zipRead, zipWrite, refToCell, colToName, serialToString, dateToDateSerial, normalizeRows };