@efa-one/sdk 1.21.0 → 1.23.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/README.md +78 -3
- package/backend/auth.d.ts +40 -1
- package/backend/auth.js +194 -46
- package/backend/index.d.ts +2 -0
- package/backend/index.js +2 -0
- package/backend/permissionCheck.js +2 -1
- package/backend/permissionClient.d.ts +1 -0
- package/backend/permissionClient.js +3 -1
- package/backend/permissions.js +3 -10
- package/backend/platformAdmin.d.ts +4 -0
- package/backend/platformAdmin.js +16 -0
- package/backend/serviceClient.d.ts +10 -1
- package/backend/serviceClient.js +67 -34
- package/backend/serviceProvenance.d.ts +31 -0
- package/backend/serviceProvenance.js +137 -0
- package/frontend/index.d.ts +1 -0
- package/frontend/index.js +1 -0
- package/frontend/ipc.js +3 -1
- package/frontend/platformAdmin.d.ts +4 -0
- package/frontend/platformAdmin.js +11 -0
- package/frontend/ui/DataTable.d.ts +10 -2
- package/frontend/ui/DataTable.js +65 -8
- package/frontend/ui/index.d.ts +1 -0
- package/frontend/ui/index.js +1 -0
- package/frontend/ui/tableExport.d.ts +22 -0
- package/frontend/ui/tableExport.js +254 -0
- package/package.json +6 -6
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { formatDate, formatDateTime } from '../format.js';
|
|
2
|
+
export const CSV_SEPARATOR = ';';
|
|
3
|
+
function neutralizeFormula(text) {
|
|
4
|
+
return /^[=+\-@\t\r]/.test(text) ? `'${text}` : text;
|
|
5
|
+
}
|
|
6
|
+
function csvField(cell) {
|
|
7
|
+
let text;
|
|
8
|
+
if (cell == null)
|
|
9
|
+
text = '';
|
|
10
|
+
else if (typeof cell === 'number')
|
|
11
|
+
text = String(cell).replace('.', ',');
|
|
12
|
+
else if (typeof cell === 'string')
|
|
13
|
+
text = neutralizeFormula(cell);
|
|
14
|
+
else
|
|
15
|
+
text = cell.withTime ? formatDateTime(cell.date) : formatDate(cell.date);
|
|
16
|
+
return /[";\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
17
|
+
}
|
|
18
|
+
export function toCsv(table) {
|
|
19
|
+
const lines = [table.headers.map(csvField), ...table.rows.map((r) => r.map(csvField))];
|
|
20
|
+
return '' + lines.map((l) => l.join(CSV_SEPARATOR)).join('\r\n') + '\r\n';
|
|
21
|
+
}
|
|
22
|
+
const XLSX_MAX_CELL_CHARS = 32767;
|
|
23
|
+
function xmlEscape(text) {
|
|
24
|
+
return text
|
|
25
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '')
|
|
26
|
+
.replace(/&/g, '&')
|
|
27
|
+
.replace(/</g, '<')
|
|
28
|
+
.replace(/>/g, '>')
|
|
29
|
+
.replace(/"/g, '"');
|
|
30
|
+
}
|
|
31
|
+
export function columnLetter(index) {
|
|
32
|
+
let n = index + 1;
|
|
33
|
+
let s = '';
|
|
34
|
+
while (n > 0) {
|
|
35
|
+
const r = (n - 1) % 26;
|
|
36
|
+
s = String.fromCharCode(65 + r) + s;
|
|
37
|
+
n = Math.floor((n - 1) / 26);
|
|
38
|
+
}
|
|
39
|
+
return s;
|
|
40
|
+
}
|
|
41
|
+
export function excelSerial(date, withTime) {
|
|
42
|
+
const localMs = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), withTime ? date.getHours() : 0, withTime ? date.getMinutes() : 0, withTime ? date.getSeconds() : 0);
|
|
43
|
+
return localMs / 86400000 + 25569;
|
|
44
|
+
}
|
|
45
|
+
const STYLE_HEADER = 1;
|
|
46
|
+
const STYLE_DATE = 2;
|
|
47
|
+
const STYLE_DATETIME = 3;
|
|
48
|
+
const STYLES_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
49
|
+
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
|
50
|
+
<numFmts count="2"><numFmt numFmtId="164" formatCode="dd.mm.yyyy"/><numFmt numFmtId="165" formatCode="dd.mm.yyyy hh:mm"/></numFmts>
|
|
51
|
+
<fonts count="2"><font><sz val="11"/><name val="Calibri"/><family val="2"/></font><font><b/><sz val="11"/><name val="Calibri"/><family val="2"/></font></fonts>
|
|
52
|
+
<fills count="3"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FFE7E7E7"/><bgColor indexed="64"/></patternFill></fill></fills>
|
|
53
|
+
<borders count="2"><border><left/><right/><top/><bottom/><diagonal/></border><border><left style="thin"><color rgb="FFBFBFBF"/></left><right style="thin"><color rgb="FFBFBFBF"/></right><top style="thin"><color rgb="FFBFBFBF"/></top><bottom style="medium"><color rgb="FF808080"/></bottom><diagonal/></border></borders>
|
|
54
|
+
<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>
|
|
55
|
+
<cellXfs count="4">
|
|
56
|
+
<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
|
|
57
|
+
<xf numFmtId="0" fontId="1" fillId="2" borderId="1" xfId="0" applyFont="1" applyFill="1" applyBorder="1" applyAlignment="1"><alignment vertical="center"/></xf>
|
|
58
|
+
<xf numFmtId="164" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/>
|
|
59
|
+
<xf numFmtId="165" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/>
|
|
60
|
+
</cellXfs>
|
|
61
|
+
<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>
|
|
62
|
+
</styleSheet>`;
|
|
63
|
+
const CONTENT_TYPES_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
64
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
65
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
66
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
|
67
|
+
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
|
|
68
|
+
<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
|
|
69
|
+
<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
|
|
70
|
+
</Types>`;
|
|
71
|
+
const ROOT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
72
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
73
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
|
|
74
|
+
</Relationships>`;
|
|
75
|
+
const WORKBOOK_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
76
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
77
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
|
|
78
|
+
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
|
79
|
+
</Relationships>`;
|
|
80
|
+
export function trimChar(s, ch) {
|
|
81
|
+
let start = 0;
|
|
82
|
+
let end = s.length;
|
|
83
|
+
while (start < end && s[start] === ch)
|
|
84
|
+
start++;
|
|
85
|
+
while (end > start && s[end - 1] === ch)
|
|
86
|
+
end--;
|
|
87
|
+
return s.slice(start, end);
|
|
88
|
+
}
|
|
89
|
+
export function sanitizeSheetName(name) {
|
|
90
|
+
const cleaned = trimChar(name.replace(/[[\]:*?/\\]/g, ' '), "'").trim().slice(0, 31).trim();
|
|
91
|
+
return cleaned || 'Export';
|
|
92
|
+
}
|
|
93
|
+
function textCell(ref, text, style) {
|
|
94
|
+
const s = style != null ? ` s="${style}"` : '';
|
|
95
|
+
return `<c r="${ref}" t="inlineStr"${s}><is><t xml:space="preserve">${xmlEscape(text.slice(0, XLSX_MAX_CELL_CHARS))}</t></is></c>`;
|
|
96
|
+
}
|
|
97
|
+
function dataCell(ref, cell) {
|
|
98
|
+
if (cell == null)
|
|
99
|
+
return '';
|
|
100
|
+
if (typeof cell === 'number')
|
|
101
|
+
return `<c r="${ref}"><v>${cell}</v></c>`;
|
|
102
|
+
if (typeof cell === 'string')
|
|
103
|
+
return cell === '' ? '' : textCell(ref, cell);
|
|
104
|
+
const serial = excelSerial(cell.date, cell.withTime);
|
|
105
|
+
if (serial < 61)
|
|
106
|
+
return textCell(ref, cell.withTime ? formatDateTime(cell.date) : formatDate(cell.date));
|
|
107
|
+
return `<c r="${ref}" s="${cell.withTime ? STYLE_DATETIME : STYLE_DATE}"><v>${serial}</v></c>`;
|
|
108
|
+
}
|
|
109
|
+
function displayLength(cell) {
|
|
110
|
+
if (cell == null)
|
|
111
|
+
return 0;
|
|
112
|
+
if (typeof cell === 'number')
|
|
113
|
+
return String(cell).length;
|
|
114
|
+
if (typeof cell === 'string')
|
|
115
|
+
return Math.max(...cell.split('\n').map((l) => l.length));
|
|
116
|
+
return cell.withTime ? 16 : 10;
|
|
117
|
+
}
|
|
118
|
+
function sheetXml(table) {
|
|
119
|
+
const colCount = Math.max(table.headers.length, 1);
|
|
120
|
+
const lastCol = columnLetter(colCount - 1);
|
|
121
|
+
const lastRow = table.rows.length + 1;
|
|
122
|
+
const widths = table.headers.map((h, ci) => {
|
|
123
|
+
let max = h.length + 3;
|
|
124
|
+
for (const r of table.rows)
|
|
125
|
+
max = Math.max(max, displayLength(r[ci] ?? null));
|
|
126
|
+
return Math.min(60, Math.max(8, max + 2));
|
|
127
|
+
});
|
|
128
|
+
const header = `<row r="1">${table.headers.map((h, ci) => textCell(`${columnLetter(ci)}1`, h, STYLE_HEADER)).join('')}</row>`;
|
|
129
|
+
const body = table.rows.map((r, ri) => {
|
|
130
|
+
const rowNo = ri + 2;
|
|
131
|
+
return `<row r="${rowNo}">${r.map((c, ci) => dataCell(`${columnLetter(ci)}${rowNo}`, c)).join('')}</row>`;
|
|
132
|
+
}).join('');
|
|
133
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
134
|
+
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
135
|
+
<dimension ref="A1:${lastCol}${lastRow}"/>
|
|
136
|
+
<sheetViews><sheetView workbookViewId="0"><pane ySplit="1" topLeftCell="A2" activePane="bottomLeft" state="frozen"/></sheetView></sheetViews>
|
|
137
|
+
<sheetFormatPr defaultRowHeight="15"/>
|
|
138
|
+
${widths.length > 0 ? `<cols>${widths.map((w, i) => `<col min="${i + 1}" max="${i + 1}" width="${w}" customWidth="1"/>`).join('')}</cols>` : ''}
|
|
139
|
+
<sheetData>${header}${body}</sheetData>
|
|
140
|
+
${table.headers.length > 0 ? `<autoFilter ref="A1:${lastCol}${lastRow}"/>` : ''}
|
|
141
|
+
</worksheet>`;
|
|
142
|
+
}
|
|
143
|
+
function workbookXml(sheetName, table) {
|
|
144
|
+
const lastCol = columnLetter(Math.max(table.headers.length, 1) - 1);
|
|
145
|
+
const quoted = `'${sheetName.replace(/'/g, "''")}'`;
|
|
146
|
+
const filterName = table.headers.length > 0
|
|
147
|
+
? `<definedNames><definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">${xmlEscape(quoted)}!$A$1:$${lastCol}$${table.rows.length + 1}</definedName></definedNames>`
|
|
148
|
+
: '';
|
|
149
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
150
|
+
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
151
|
+
<sheets><sheet name="${xmlEscape(sheetName)}" sheetId="1" r:id="rId1"/></sheets>
|
|
152
|
+
${filterName}
|
|
153
|
+
</workbook>`;
|
|
154
|
+
}
|
|
155
|
+
export function toXlsx(table, sheetName = 'Export') {
|
|
156
|
+
const name = sanitizeSheetName(sheetName);
|
|
157
|
+
return zipStore([
|
|
158
|
+
{ name: '[Content_Types].xml', data: CONTENT_TYPES_XML },
|
|
159
|
+
{ name: '_rels/.rels', data: ROOT_RELS_XML },
|
|
160
|
+
{ name: 'xl/workbook.xml', data: workbookXml(name, table) },
|
|
161
|
+
{ name: 'xl/_rels/workbook.xml.rels', data: WORKBOOK_RELS_XML },
|
|
162
|
+
{ name: 'xl/styles.xml', data: STYLES_XML },
|
|
163
|
+
{ name: 'xl/worksheets/sheet1.xml', data: sheetXml(table) },
|
|
164
|
+
]);
|
|
165
|
+
}
|
|
166
|
+
let crcTable = null;
|
|
167
|
+
export function crc32(bytes) {
|
|
168
|
+
if (!crcTable) {
|
|
169
|
+
crcTable = new Uint32Array(256);
|
|
170
|
+
for (let n = 0; n < 256; n++) {
|
|
171
|
+
let c = n;
|
|
172
|
+
for (let k = 0; k < 8; k++)
|
|
173
|
+
c = c & 1 ? 0xEDB88320 ^ (c >>> 1) : c >>> 1;
|
|
174
|
+
crcTable[n] = c >>> 0;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
let crc = 0xFFFFFFFF;
|
|
178
|
+
for (let i = 0; i < bytes.length; i++)
|
|
179
|
+
crc = crcTable[(crc ^ bytes[i]) & 0xFF] ^ (crc >>> 8);
|
|
180
|
+
return (crc ^ 0xFFFFFFFF) >>> 0;
|
|
181
|
+
}
|
|
182
|
+
export function zipStore(files) {
|
|
183
|
+
const enc = new TextEncoder();
|
|
184
|
+
const dosTime = 0;
|
|
185
|
+
const dosDate = ((2026 - 1980) << 9) | (1 << 5) | 1;
|
|
186
|
+
const locals = [];
|
|
187
|
+
const centrals = [];
|
|
188
|
+
let offset = 0;
|
|
189
|
+
for (const f of files) {
|
|
190
|
+
const name = enc.encode(f.name);
|
|
191
|
+
const data = enc.encode(f.data);
|
|
192
|
+
const crc = crc32(data);
|
|
193
|
+
const local = new Uint8Array(30 + name.length + data.length);
|
|
194
|
+
const lv = new DataView(local.buffer);
|
|
195
|
+
lv.setUint32(0, 0x04034B50, true);
|
|
196
|
+
lv.setUint16(4, 20, true);
|
|
197
|
+
lv.setUint16(6, 0x0800, true);
|
|
198
|
+
lv.setUint16(8, 0, true);
|
|
199
|
+
lv.setUint16(10, dosTime, true);
|
|
200
|
+
lv.setUint16(12, dosDate, true);
|
|
201
|
+
lv.setUint32(14, crc, true);
|
|
202
|
+
lv.setUint32(18, data.length, true);
|
|
203
|
+
lv.setUint32(22, data.length, true);
|
|
204
|
+
lv.setUint16(26, name.length, true);
|
|
205
|
+
lv.setUint16(28, 0, true);
|
|
206
|
+
local.set(name, 30);
|
|
207
|
+
local.set(data, 30 + name.length);
|
|
208
|
+
locals.push(local);
|
|
209
|
+
const central = new Uint8Array(46 + name.length);
|
|
210
|
+
const cv = new DataView(central.buffer);
|
|
211
|
+
cv.setUint32(0, 0x02014B50, true);
|
|
212
|
+
cv.setUint16(4, 20, true);
|
|
213
|
+
cv.setUint16(6, 20, true);
|
|
214
|
+
cv.setUint16(8, 0x0800, true);
|
|
215
|
+
cv.setUint16(10, 0, true);
|
|
216
|
+
cv.setUint16(12, dosTime, true);
|
|
217
|
+
cv.setUint16(14, dosDate, true);
|
|
218
|
+
cv.setUint32(16, crc, true);
|
|
219
|
+
cv.setUint32(20, data.length, true);
|
|
220
|
+
cv.setUint32(24, data.length, true);
|
|
221
|
+
cv.setUint16(28, name.length, true);
|
|
222
|
+
cv.setUint32(42, offset, true);
|
|
223
|
+
central.set(name, 46);
|
|
224
|
+
centrals.push(central);
|
|
225
|
+
offset += local.length;
|
|
226
|
+
}
|
|
227
|
+
const centralSize = centrals.reduce((s, c) => s + c.length, 0);
|
|
228
|
+
const end = new Uint8Array(22);
|
|
229
|
+
const ev = new DataView(end.buffer);
|
|
230
|
+
ev.setUint32(0, 0x06054B50, true);
|
|
231
|
+
ev.setUint16(8, files.length, true);
|
|
232
|
+
ev.setUint16(10, files.length, true);
|
|
233
|
+
ev.setUint32(12, centralSize, true);
|
|
234
|
+
ev.setUint32(16, offset, true);
|
|
235
|
+
const out = new Uint8Array(offset + centralSize + end.length);
|
|
236
|
+
let pos = 0;
|
|
237
|
+
for (const part of [...locals, ...centrals, end]) {
|
|
238
|
+
out.set(part, pos);
|
|
239
|
+
pos += part.length;
|
|
240
|
+
}
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
export const XLSX_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
244
|
+
export function downloadFile(data, fileName, mime) {
|
|
245
|
+
const url = URL.createObjectURL(new Blob([data], { type: mime }));
|
|
246
|
+
const a = document.createElement('a');
|
|
247
|
+
a.href = url;
|
|
248
|
+
a.download = fileName;
|
|
249
|
+
a.style.display = 'none';
|
|
250
|
+
document.body.appendChild(a);
|
|
251
|
+
a.click();
|
|
252
|
+
a.remove();
|
|
253
|
+
window.setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
254
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@efa-one/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "Offizielles SDK für efa-one-Apps: Auth/Session, Service-Discovery, Gateway-Client, Audit/Reporting/Mail/Notifications, Permission- und Capability-Registrierung sowie das Frontend-IPC-/i18n-Toolkit.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@radix-ui/react-tooltip": ">=1",
|
|
59
59
|
"express": ">=4",
|
|
60
60
|
"i18next": ">=23",
|
|
61
|
-
"i18next-http-backend": ">=2",
|
|
61
|
+
"i18next-http-backend": ">=4.0.2",
|
|
62
62
|
"jsonwebtoken": ">=9",
|
|
63
63
|
"lucide-react": ">=0.400.0",
|
|
64
64
|
"pg": ">=8",
|
|
@@ -111,18 +111,18 @@
|
|
|
111
111
|
"@radix-ui/react-tooltip": "^1.1.6",
|
|
112
112
|
"@types/express": "^4.17.21",
|
|
113
113
|
"@types/jsonwebtoken": "^9.0.6",
|
|
114
|
-
"@types/node": "^20.
|
|
114
|
+
"@types/node": "^22.20.5",
|
|
115
115
|
"@types/pg": "^8.11.0",
|
|
116
116
|
"@types/react": "^18.2.46",
|
|
117
117
|
"@types/react-dom": "^18.2.0",
|
|
118
118
|
"@types/supertest": "^7.2.0",
|
|
119
|
-
"@vitest/coverage-v8": "^
|
|
119
|
+
"@vitest/coverage-v8": "^5.0.5",
|
|
120
120
|
"cookie-parser": "^1.4.6",
|
|
121
121
|
"eslint": "^10.9.1",
|
|
122
122
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
123
123
|
"express": "^4.19.2",
|
|
124
124
|
"i18next": "^23.11.0",
|
|
125
|
-
"i18next-http-backend": "^
|
|
125
|
+
"i18next-http-backend": "^4.0.2",
|
|
126
126
|
"jsonwebtoken": "^9.0.2",
|
|
127
127
|
"lucide-react": "^0.577.0",
|
|
128
128
|
"pg": "^8.12.0",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"supertest": "^7.2.2",
|
|
133
133
|
"typescript": "6.0.3",
|
|
134
134
|
"typescript-eslint": "^8.67.0",
|
|
135
|
-
"vitest": "^
|
|
135
|
+
"vitest": "^5.0.5"
|
|
136
136
|
},
|
|
137
137
|
"engines": {
|
|
138
138
|
"node": ">=20"
|