@xeplr/actions 1.1.18 → 1.1.20
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 +57 -2
- package/index.js +2 -0
- package/lib/builtins/export/pages.js +92 -0
- package/lib/builtins/export/table.js +116 -0
- package/lib/builtins/index.js +8 -1
- package/lib/drivers/db/dateValues.js +106 -0
- package/lib/drivers/db/duckdb.js +32 -20
- package/lib/drivers/db/mssql.js +21 -32
- package/lib/drivers/db/mysql.js +23 -32
- package/lib/drivers/db/postgres.js +32 -23
- package/lib/formats/index.js +3 -1
- package/lib/formats/writers/csv.js +78 -0
- package/lib/formats/writers/index.js +23 -0
- package/lib/formats/writers/pdf.js +293 -0
- package/lib/formats/writers/xlsx.js +284 -0
- package/lib/uploader/index.js +14 -0
- package/lib/uploader/reconcile.js +2 -1
- package/package.json +23 -4
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// .xlsx writer — our own, on fflate's streaming zip.
|
|
2
|
+
//
|
|
3
|
+
// Why not exceljs: it pulls a uuid with a published advisory, has not had a
|
|
4
|
+
// release since December 2024, and the override that silences it has to be
|
|
5
|
+
// repeated in every app that installs this package. Writing SpreadsheetML is
|
|
6
|
+
// the other half of what lib/formats/_xlsx-stream.js already reads, and a
|
|
7
|
+
// workbook of one styled sheet is a small, well-specified thing.
|
|
8
|
+
//
|
|
9
|
+
// WHAT IT WRITES: one sheet; an optional title and subtitle above the header;
|
|
10
|
+
// the header bold on a tint, frozen, with an autofilter; numbers and dates as
|
|
11
|
+
// REAL cells carrying their number format, so the reader can still sum and
|
|
12
|
+
// filter; subtotal rows bold, total rows bold on a tint; column widths from
|
|
13
|
+
// the header and the first rows.
|
|
14
|
+
//
|
|
15
|
+
// Streaming: rows go straight into the zip, which goes straight to disk,
|
|
16
|
+
// waiting on the file stream when it is full — a million rows costs the
|
|
17
|
+
// same memory as a hundred. Only the first SAMPLE_ROWS are held, to size the
|
|
18
|
+
// columns, because <cols> must come before the rows in the sheet.
|
|
19
|
+
|
|
20
|
+
var dateValues = require('../../drivers/db/dateValues');
|
|
21
|
+
var fs = require('fs');
|
|
22
|
+
|
|
23
|
+
var MAX_ROWS = 1048576; // Excel's own limit, header and title rows included
|
|
24
|
+
var SAMPLE_ROWS = 200;
|
|
25
|
+
var CHUNK = 1 << 20; // push to the zip in ~1 MB pieces
|
|
26
|
+
|
|
27
|
+
// XML 1.0 cannot carry most control characters even escaped; Excel refuses
|
|
28
|
+
// the file ("We found a problem with some content"). Dropped.
|
|
29
|
+
var INVALID_XML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g;
|
|
30
|
+
function esc(value) {
|
|
31
|
+
return String(value).replace(INVALID_XML, '')
|
|
32
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 0 → A, 25 → Z, 26 → AA. */
|
|
36
|
+
function colName(i) {
|
|
37
|
+
var s = '';
|
|
38
|
+
for (var n = i + 1; n > 0; n = Math.floor((n - 1) / 26)) s = String.fromCharCode(65 + ((n - 1) % 26)) + s;
|
|
39
|
+
return s;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A Date, or an ISO date string, as an Excel serial — or null. A string with
|
|
44
|
+
* no zone is read as UTC (dateValues.toInstant), never in this machine's zone:
|
|
45
|
+
* `new Date('2025-08-01 10:00')` would put a different time in the cell on a
|
|
46
|
+
* server in India than on one in New York.
|
|
47
|
+
*/
|
|
48
|
+
function dateSerial(value) {
|
|
49
|
+
var d = value instanceof Date ? value : (typeof value === 'string' ? dateValues.toInstant(value) : null);
|
|
50
|
+
if (!d || isNaN(d.getTime())) return null;
|
|
51
|
+
return d.getTime() / 86400000 + 25569;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Fonts: 0 normal, 1 bold, 2 title. Fills: 0 none, 1 gray125 (both required
|
|
55
|
+
// by Excel at those positions), 2 header tint, 3 total tint.
|
|
56
|
+
var FONT = { normal: 0, bold: 1, title: 2 };
|
|
57
|
+
var FILL = { none: 0, header: 2, total: 3 };
|
|
58
|
+
|
|
59
|
+
function stylesXml(numFmts, xfs) {
|
|
60
|
+
var fmts = numFmts.map(function(code, i) { return '<numFmt numFmtId="' + (164 + i) + '" formatCode="' + esc(code) + '"/>'; });
|
|
61
|
+
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
62
|
+
'<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">' +
|
|
63
|
+
(fmts.length ? '<numFmts count="' + fmts.length + '">' + fmts.join('') + '</numFmts>' : '') +
|
|
64
|
+
'<fonts count="3"><font><sz val="11"/><name val="Calibri"/></font><font><b/><sz val="11"/><name val="Calibri"/></font>' +
|
|
65
|
+
'<font><b/><sz val="14"/><name val="Calibri"/></font></fonts>' +
|
|
66
|
+
'<fills count="4"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill>' +
|
|
67
|
+
'<fill><patternFill patternType="solid"><fgColor rgb="FFF1F3F7"/><bgColor indexed="64"/></patternFill></fill>' +
|
|
68
|
+
'<fill><patternFill patternType="solid"><fgColor rgb="FFE8EBF2"/><bgColor indexed="64"/></patternFill></fill></fills>' +
|
|
69
|
+
'<borders count="2"><border><left/><right/><top/><bottom/><diagonal/></border>' +
|
|
70
|
+
'<border><left/><right/><top style="thin"><color rgb="FF9AA1AE"/></top><bottom/><diagonal/></border></borders>' +
|
|
71
|
+
'<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>' +
|
|
72
|
+
'<cellXfs count="' + xfs.length + '">' + xfs.map(function(x) {
|
|
73
|
+
return '<xf numFmtId="' + x.numFmtId + '" fontId="' + x.fontId + '" fillId="' + x.fillId + '" borderId="' + x.borderId + '" xfId="0"' +
|
|
74
|
+
(x.numFmtId ? ' applyNumberFormat="1"' : '') + (x.fontId ? ' applyFont="1"' : '') + (x.fillId ? ' applyFill="1"' : '') +
|
|
75
|
+
(x.borderId ? ' applyBorder="1"' : '') + (x.align ? ' applyAlignment="1"><alignment horizontal="' + x.align + '"/></xf>' : '/>');
|
|
76
|
+
}).join('') + '</cellXfs>' +
|
|
77
|
+
'<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
var STATIC = {
|
|
81
|
+
'[Content_Types].xml': '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
82
|
+
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
|
|
83
|
+
'<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>' +
|
|
84
|
+
'<Default Extension="xml" ContentType="application/xml"/>' +
|
|
85
|
+
'<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>' +
|
|
86
|
+
'<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>' +
|
|
87
|
+
'<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>' +
|
|
88
|
+
'</Types>',
|
|
89
|
+
'_rels/.rels': '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
90
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
|
|
91
|
+
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' +
|
|
92
|
+
'</Relationships>',
|
|
93
|
+
'xl/_rels/workbook.xml.rels': '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
94
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
|
|
95
|
+
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>' +
|
|
96
|
+
'<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' +
|
|
97
|
+
'</Relationships>'
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function workbookXml(sheetName, filterRef) {
|
|
101
|
+
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
102
|
+
'<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' +
|
|
103
|
+
'<sheets><sheet name="' + esc(sheetName) + '" sheetId="1" r:id="rId1"/></sheets>' +
|
|
104
|
+
(filterRef ? '<definedNames><definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">' + esc("'" + sheetName.replace(/'/g, "''") + "'!" + filterRef) + '</definedName></definedNames>' : '') +
|
|
105
|
+
'</workbook>';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Excel refuses a sheet name over 31 characters or with any of : \ / ? * [ ] */
|
|
109
|
+
function sheetNameOf(title) {
|
|
110
|
+
var name = String(title || 'Sheet1').replace(/[:\\/?*[\]]/g, ' ').trim().slice(0, 31);
|
|
111
|
+
return name || 'Sheet1';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
requires: ['fflate'],
|
|
116
|
+
MAX_ROWS: MAX_ROWS,
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {string} filePath
|
|
120
|
+
* @param {object} doc - { title?, subtitle?, columns: [{ header, kind?: 'number'|'date'|'text'|'raw', numFmt?, width?, align? }] }
|
|
121
|
+
* kind 'number': the value is written as a number (numFmt applies);
|
|
122
|
+
* kind 'date': a Date or ISO string, written as a date serial (numFmt applies);
|
|
123
|
+
* anything else: a number stays a number, everything else is text.
|
|
124
|
+
* @returns {{ writeRow(row): Promise<boolean>, close(): Promise<{ rows, truncated }> }}
|
|
125
|
+
* row: { values: [], kind?: 'detail'|'subtotal'|'total' }. writeRow answers
|
|
126
|
+
* false once Excel's row limit is reached (the rest are counted, not written).
|
|
127
|
+
*/
|
|
128
|
+
open: function(filePath, doc) {
|
|
129
|
+
var fflate = require('fflate');
|
|
130
|
+
var columns = doc.columns || [];
|
|
131
|
+
var out = fs.createWriteStream(filePath);
|
|
132
|
+
var failed = null;
|
|
133
|
+
out.on('error', function(err) { failed = err; });
|
|
134
|
+
var zipDone;
|
|
135
|
+
var zipFinished = new Promise(function(resolve) { zipDone = resolve; });
|
|
136
|
+
var zip = new fflate.Zip(function(err, chunk, final) {
|
|
137
|
+
if (err) { failed = err; return; }
|
|
138
|
+
out.write(Buffer.from(chunk));
|
|
139
|
+
if (final) zipDone();
|
|
140
|
+
});
|
|
141
|
+
function addFile(name, text) {
|
|
142
|
+
var f = new fflate.ZipDeflate(name, { level: 6 });
|
|
143
|
+
zip.add(f);
|
|
144
|
+
f.push(fflate.strToU8(text), true);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Styles are collected as the rows use them and written at the end.
|
|
148
|
+
var numFmts = [];
|
|
149
|
+
var xfs = [{ numFmtId: 0, fontId: 0, fillId: 0, borderId: 0 }];
|
|
150
|
+
var xfIndex = { '0|0|0|0|': 0 };
|
|
151
|
+
function styleId(numFmt, fontId, fillId, borderId, align) {
|
|
152
|
+
var numFmtId = 0;
|
|
153
|
+
if (numFmt) {
|
|
154
|
+
var at = numFmts.indexOf(numFmt);
|
|
155
|
+
if (at < 0) { numFmts.push(numFmt); at = numFmts.length - 1; }
|
|
156
|
+
numFmtId = 164 + at;
|
|
157
|
+
}
|
|
158
|
+
var key = numFmtId + '|' + fontId + '|' + fillId + '|' + borderId + '|' + (align || '');
|
|
159
|
+
if (xfIndex[key] === undefined) {
|
|
160
|
+
xfs.push({ numFmtId: numFmtId, fontId: fontId, fillId: fillId, borderId: borderId, align: align || null });
|
|
161
|
+
xfIndex[key] = xfs.length - 1;
|
|
162
|
+
}
|
|
163
|
+
return xfIndex[key];
|
|
164
|
+
}
|
|
165
|
+
var LOOK = {
|
|
166
|
+
detail: { font: FONT.normal, fill: FILL.none, border: 0 },
|
|
167
|
+
subtotal: { font: FONT.bold, fill: FILL.none, border: 0 },
|
|
168
|
+
total: { font: FONT.bold, fill: FILL.total, border: 1 }
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
var sheet = new fflate.ZipDeflate('xl/worksheets/sheet1.xml', { level: 6 });
|
|
172
|
+
var buffered = '';
|
|
173
|
+
var rowNo = 0;
|
|
174
|
+
var written = 0; // data rows written
|
|
175
|
+
var offered = 0; // data rows asked for
|
|
176
|
+
var headerRow = 0;
|
|
177
|
+
var sample = [];
|
|
178
|
+
var started = false;
|
|
179
|
+
|
|
180
|
+
function cellXml(ref, value, column, look) {
|
|
181
|
+
if (value === null || value === undefined || value === '') return '';
|
|
182
|
+
var kind = column.kind;
|
|
183
|
+
var align = column.align === 'right' || column.align === 'center' ? column.align : null;
|
|
184
|
+
if (kind === 'date') {
|
|
185
|
+
var serial = dateSerial(value);
|
|
186
|
+
if (serial !== null) return '<c r="' + ref + '" s="' + styleId(column.numFmt || 'yyyy-mm-dd', look.font, look.fill, look.border, align) + '"><v>' + serial + '</v></c>';
|
|
187
|
+
}
|
|
188
|
+
var num = typeof value === 'number' ? value : (kind === 'number' && value !== '' && !isNaN(Number(value)) ? Number(value) : null);
|
|
189
|
+
if (num !== null && isFinite(num)) {
|
|
190
|
+
return '<c r="' + ref + '" s="' + styleId(kind === 'number' ? column.numFmt : null, look.font, look.fill, look.border, align) + '"><v>' + num + '</v></c>';
|
|
191
|
+
}
|
|
192
|
+
if (typeof value === 'boolean') return '<c r="' + ref + '" t="b" s="' + styleId(null, look.font, look.fill, look.border, align) + '"><v>' + (value ? 1 : 0) + '</v></c>';
|
|
193
|
+
return '<c r="' + ref + '" t="inlineStr" s="' + styleId(null, look.font, look.fill, look.border, align) + '"><is><t xml:space="preserve">' + esc(value) + '</t></is></c>';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function textRow(cells) { rowNo += 1; return '<row r="' + rowNo + '">' + cells + '</row>'; }
|
|
197
|
+
|
|
198
|
+
async function push(text, final) {
|
|
199
|
+
buffered += text;
|
|
200
|
+
if (buffered.length < CHUNK && !final) return;
|
|
201
|
+
sheet.push(fflate.strToU8(buffered), Boolean(final));
|
|
202
|
+
buffered = '';
|
|
203
|
+
if (failed) throw failed;
|
|
204
|
+
if (out.writableNeedDrain) await new Promise(function(r) { out.once('drain', r); });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// <cols> needs the widths, which need a look at the rows: written once
|
|
208
|
+
// the sample is full, or at close for a short table.
|
|
209
|
+
async function start() {
|
|
210
|
+
started = true;
|
|
211
|
+
var widths = columns.map(function(c, i) {
|
|
212
|
+
if (c.width) return c.width;
|
|
213
|
+
var longest = String(c.header || '').length;
|
|
214
|
+
// A date is as wide as its pattern, not as its JS string form.
|
|
215
|
+
if (c.kind === 'date') return Math.min(60, Math.max(10, longest, String(c.numFmt || 'yyyy-mm-dd').length) + 2);
|
|
216
|
+
sample.forEach(function(r) {
|
|
217
|
+
var shown = r.display ? r.display[i] : r.values[i];
|
|
218
|
+
if (shown !== null && shown !== undefined) longest = Math.max(longest, String(shown).length);
|
|
219
|
+
});
|
|
220
|
+
return Math.min(60, Math.max(8, longest + 2));
|
|
221
|
+
});
|
|
222
|
+
zip.add(sheet);
|
|
223
|
+
var head = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
224
|
+
'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">';
|
|
225
|
+
var titleRows = (doc.title ? 1 : 0) + (doc.subtitle ? 1 : 0);
|
|
226
|
+
headerRow = titleRows + (titleRows ? 2 : 1);
|
|
227
|
+
head += '<sheetViews><sheetView workbookViewId="0"><pane ySplit="' + headerRow + '" topLeftCell="A' + (headerRow + 1) +
|
|
228
|
+
'" activePane="bottomLeft" state="frozen"/></sheetView></sheetViews>';
|
|
229
|
+
head += '<cols>' + widths.map(function(w, i) { return '<col min="' + (i + 1) + '" max="' + (i + 1) + '" width="' + w + '" customWidth="1"/>'; }).join('') + '</cols>';
|
|
230
|
+
head += '<sheetData>';
|
|
231
|
+
if (doc.title) head += textRow('<c r="A' + (rowNo + 1) + '" t="inlineStr" s="' + styleId(null, FONT.title, 0, 0) + '"><is><t xml:space="preserve">' + esc(doc.title) + '</t></is></c>');
|
|
232
|
+
if (doc.subtitle) head += textRow('<c r="A' + (rowNo + 1) + '" t="inlineStr"><is><t xml:space="preserve">' + esc(doc.subtitle) + '</t></is></c>');
|
|
233
|
+
if (titleRows) rowNo += 1; // a blank row between the title block and the table
|
|
234
|
+
head += textRow(columns.map(function(c, i) {
|
|
235
|
+
var align = c.align === 'right' || c.align === 'center' ? c.align : null;
|
|
236
|
+
return '<c r="' + colName(i) + (rowNo + 1) + '" t="inlineStr" s="' + styleId(null, FONT.bold, FILL.header, 0, align) + '"><is><t xml:space="preserve">' + esc(c.header == null ? '' : c.header) + '</t></is></c>';
|
|
237
|
+
}).join(''));
|
|
238
|
+
await push(head);
|
|
239
|
+
var held = sample;
|
|
240
|
+
sample = null;
|
|
241
|
+
for (var k = 0; k < held.length; k++) await emit(held[k]);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function emit(row) {
|
|
245
|
+
if (rowNo >= MAX_ROWS) return false;
|
|
246
|
+
var look = LOOK[row.kind] || LOOK.detail;
|
|
247
|
+
var r = rowNo + 1;
|
|
248
|
+
var cells = '';
|
|
249
|
+
var values = row.values || [];
|
|
250
|
+
for (var i = 0; i < columns.length; i++) cells += cellXml(colName(i) + r, values[i], columns[i], look);
|
|
251
|
+
written += 1;
|
|
252
|
+
await push(textRow(cells));
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
writeRow: async function(row) {
|
|
258
|
+
offered += 1;
|
|
259
|
+
if (!started) {
|
|
260
|
+
sample.push(row);
|
|
261
|
+
if (sample.length >= SAMPLE_ROWS) await start();
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
return emit(row);
|
|
265
|
+
},
|
|
266
|
+
close: async function() {
|
|
267
|
+
if (!started) await start();
|
|
268
|
+
var lastRow = Math.max(rowNo, headerRow);
|
|
269
|
+
var filterRef = columns.length ? 'A' + headerRow + ':' + colName(columns.length - 1) + lastRow : null;
|
|
270
|
+
await push('</sheetData>' + (filterRef ? '<autoFilter ref="' + filterRef + '"/>' : '') +
|
|
271
|
+
'<pageSetup orientation="' + (columns.length > 6 ? 'landscape' : 'portrait') + '"/></worksheet>', true);
|
|
272
|
+
var sheetName = sheetNameOf(doc.title);
|
|
273
|
+
Object.keys(STATIC).forEach(function(name) { addFile(name, STATIC[name]); });
|
|
274
|
+
addFile('xl/workbook.xml', workbookXml(sheetName, filterRef ? '$A$' + headerRow + ':$' + colName(columns.length - 1) + '$' + lastRow : null));
|
|
275
|
+
addFile('xl/styles.xml', stylesXml(numFmts, xfs));
|
|
276
|
+
zip.end();
|
|
277
|
+
await zipFinished;
|
|
278
|
+
await new Promise(function(resolve, reject) { out.end(function(err) { return err ? reject(err) : resolve(); }); });
|
|
279
|
+
if (failed) throw failed;
|
|
280
|
+
return { rows: written, truncated: offered > written };
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
};
|
package/lib/uploader/index.js
CHANGED
|
@@ -410,6 +410,20 @@ async function runBootstrap(driver, pool, targetTable, columns, primaryKeys, sta
|
|
|
410
410
|
var { reconcileColumns } = require('./reconcile');
|
|
411
411
|
var reconciled = reconcileColumns(columns, targetSchema, driver.dataTypeToLogical);
|
|
412
412
|
|
|
413
|
+
// A ZONELESS SOURCE COLUMN IN A ZONED TARGET COLUMN. Tables created before
|
|
414
|
+
// zoneless date-times had their own type hold them as instants (the digits
|
|
415
|
+
// read as UTC). The target's type would win and keep it so forever — so it
|
|
416
|
+
// is retyped to zoneless, once, keeping those same digits. Only where the
|
|
417
|
+
// dialect has the two kinds (Postgres, DuckDB); MySQL DATETIME and SQL
|
|
418
|
+
// Server DATETIME2 are zoneless already.
|
|
419
|
+
var retype = reconciled.filter(function(c) { return c.sourceType === 'localdatetime' && c.targetType === 'datetime'; });
|
|
420
|
+
if (retype.length && typeof driver.buildRetypeToLocalDateTimeSql === 'function') {
|
|
421
|
+
for (var r = 0; r < retype.length; r++) {
|
|
422
|
+
await driver.query(pool, driver.buildRetypeToLocalDateTimeSql(targetTable, retype[r].name));
|
|
423
|
+
}
|
|
424
|
+
reconciled = reconcileColumns(columns, await driver.getTableSchema(pool, targetTable), driver.dataTypeToLogical);
|
|
425
|
+
}
|
|
426
|
+
|
|
413
427
|
var missing = reconciled.filter(function(c) { return c.missing; });
|
|
414
428
|
if (missing.length > 0) {
|
|
415
429
|
throw new Error(
|
|
@@ -29,7 +29,8 @@ var PG_TO_LOGICAL = {
|
|
|
29
29
|
'double precision': 'number',
|
|
30
30
|
'boolean': 'boolean',
|
|
31
31
|
'timestamp with time zone': 'datetime',
|
|
32
|
-
|
|
32
|
+
// No zone: copied as its digits, never as an instant (drivers/db/dateValues.js).
|
|
33
|
+
'timestamp without time zone': 'localdatetime',
|
|
33
34
|
// A DATE IS NOT A TIMESTAMP, and mapping it to one moves it.
|
|
34
35
|
//
|
|
35
36
|
// This said 'datetime', so a source `date` was created as TIMESTAMPTZ in the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xeplr/actions",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.20",
|
|
4
4
|
"description": "Named-action registry with typed inputs and safe invocation — plus built-in actions for HTTP, DB, files, email, spawn",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
"http",
|
|
17
17
|
"db",
|
|
18
18
|
"file",
|
|
19
|
-
"email"
|
|
19
|
+
"email",
|
|
20
|
+
"export",
|
|
21
|
+
"csv",
|
|
22
|
+
"xlsx",
|
|
23
|
+
"pdf"
|
|
20
24
|
],
|
|
21
25
|
"author": "xeplr",
|
|
22
26
|
"license": "MIT",
|
|
@@ -28,15 +32,17 @@
|
|
|
28
32
|
"access": "public"
|
|
29
33
|
},
|
|
30
34
|
"dependencies": {
|
|
31
|
-
"@xeplr/
|
|
32
|
-
"@xeplr/
|
|
35
|
+
"@xeplr/expression-handler": "^2.0.0",
|
|
36
|
+
"@xeplr/schema-handler": "^1.0.0"
|
|
33
37
|
},
|
|
34
38
|
"peerDependencies": {
|
|
35
39
|
"@duckdb/node-api": "1.5.5-r.3",
|
|
36
40
|
"@xeplr/email": "^1.0.0",
|
|
37
41
|
"@xeplr/utils": "^1.0.0",
|
|
38
42
|
"csv-parse": "^7.0.2",
|
|
43
|
+
"dejavu-fonts-ttf": "^2.37.3",
|
|
39
44
|
"exceljs": "^4.0.0",
|
|
45
|
+
"fflate": "^0.8.2",
|
|
40
46
|
"imapflow": "^1.0.0",
|
|
41
47
|
"knex": "^3.0.0",
|
|
42
48
|
"mailparser": "^3.0.0",
|
|
@@ -44,6 +50,7 @@
|
|
|
44
50
|
"mssql": "^11.0.0",
|
|
45
51
|
"mysql2": "^3.0.0",
|
|
46
52
|
"nodemailer": "^10.0.13",
|
|
53
|
+
"pdfkit": "^0.20.2",
|
|
47
54
|
"pg": "^8.0.0",
|
|
48
55
|
"saxes": "^6.0.0",
|
|
49
56
|
"ssh2-sftp-client": "^11.0.0",
|
|
@@ -97,15 +104,27 @@
|
|
|
97
104
|
},
|
|
98
105
|
"@xeplr/email": {
|
|
99
106
|
"optional": true
|
|
107
|
+
},
|
|
108
|
+
"fflate": {
|
|
109
|
+
"optional": true
|
|
110
|
+
},
|
|
111
|
+
"pdfkit": {
|
|
112
|
+
"optional": true
|
|
113
|
+
},
|
|
114
|
+
"dejavu-fonts-ttf": {
|
|
115
|
+
"optional": true
|
|
100
116
|
}
|
|
101
117
|
},
|
|
102
118
|
"devDependencies": {
|
|
103
119
|
"@duckdb/node-api": "^1.5.5-r.3",
|
|
104
120
|
"@xeplr/utils": "^1.0.0",
|
|
105
121
|
"csv-parse": "^7.0.2",
|
|
122
|
+
"dejavu-fonts-ttf": "^2.37.3",
|
|
106
123
|
"express": "^4.0.0",
|
|
124
|
+
"fflate": "^0.8.3",
|
|
107
125
|
"mssql": "^12.7.0",
|
|
108
126
|
"mysql2": "^3.23.1",
|
|
127
|
+
"pdfkit": "^0.20.2",
|
|
109
128
|
"pg": "^8.0.0",
|
|
110
129
|
"saxes": "^6.0.0",
|
|
111
130
|
"unzipper": "^0.12.0"
|