@kreiseck/kasseneck-api 1.19.0 → 1.21.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.
Files changed (67) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +70 -1
  3. package/dist/cjs/client/api.d.ts +19 -0
  4. package/dist/cjs/client/api.js +18 -0
  5. package/dist/cjs/client/aufrufe.d.ts +9 -6
  6. package/dist/cjs/client/aufrufe.js +29 -2
  7. package/dist/cjs/client/dep-export.d.ts +242 -0
  8. package/dist/cjs/client/dep-export.js +337 -0
  9. package/dist/cjs/client/index.d.ts +2 -1
  10. package/dist/cjs/client/index.js +21 -2
  11. package/dist/cjs/client/transport.d.ts +39 -4
  12. package/dist/cjs/client/transport.js +40 -5
  13. package/dist/cjs/index.d.ts +1 -1
  14. package/dist/cjs/index.js +20 -2
  15. package/dist/cjs/pos/dep.d.ts +10 -0
  16. package/dist/cjs/pos/dep.js +27 -0
  17. package/dist/cjs/pos/index.d.ts +1 -0
  18. package/dist/cjs/pos/index.js +5 -2
  19. package/dist/cjs/pos/texte.d.ts +109 -0
  20. package/dist/cjs/pos/texte.js +42 -0
  21. package/dist/cjs/stored/vokabular.d.ts +1 -1
  22. package/dist/cjs/stored/vokabular.js +1 -1
  23. package/dist/cjs/version.d.ts +1 -1
  24. package/dist/cjs/version.js +1 -1
  25. package/dist/esm/client/api.d.ts +19 -0
  26. package/dist/esm/client/api.js +19 -1
  27. package/dist/esm/client/aufrufe.d.ts +9 -6
  28. package/dist/esm/client/aufrufe.js +29 -2
  29. package/dist/esm/client/dep-export.d.ts +242 -0
  30. package/dist/esm/client/dep-export.js +326 -0
  31. package/dist/esm/client/index.d.ts +2 -1
  32. package/dist/esm/client/index.js +2 -1
  33. package/dist/esm/client/transport.d.ts +39 -4
  34. package/dist/esm/client/transport.js +39 -5
  35. package/dist/esm/index.d.ts +1 -1
  36. package/dist/esm/index.js +1 -1
  37. package/dist/esm/pos/dep.d.ts +10 -0
  38. package/dist/esm/pos/dep.js +24 -0
  39. package/dist/esm/pos/index.d.ts +1 -0
  40. package/dist/esm/pos/index.js +2 -0
  41. package/dist/esm/pos/texte.d.ts +109 -0
  42. package/dist/esm/pos/texte.js +42 -0
  43. package/dist/esm/stored/vokabular.d.ts +1 -1
  44. package/dist/esm/stored/vokabular.js +1 -1
  45. package/dist/esm/version.d.ts +1 -1
  46. package/dist/esm/version.js +1 -1
  47. package/fixtures/dep-folder-cases.json +14 -0
  48. package/fixtures/hobex-hps-codes.json +1 -1
  49. package/fixtures/invoice-api.schema.json +1 -1
  50. package/fixtures/invoice-texts.json +1 -1
  51. package/fixtures/pos-message-cases.json +9 -0
  52. package/fixtures/pos-texts.json +127 -1
  53. package/fixtures/surface.json +23 -3
  54. package/fixtures/v3/antworten/belege.json +1 -1
  55. package/fixtures/v3/antworten/belegmail.json +1 -1
  56. package/fixtures/v3/antworten/berichte.json +1 -1
  57. package/fixtures/v3/antworten/kasse-belege.json +1 -1
  58. package/fixtures/v3/antworten/kasse.json +802 -3
  59. package/fixtures/v3/antworten/lager.json +1 -1
  60. package/fixtures/v3/antworten/rechnungen.json +1 -1
  61. package/fixtures/v3/antworten/storno.json +1 -1
  62. package/fixtures/v3/stored/belege.json +1 -1
  63. package/fixtures/v3/stored/kasse.json +153 -6
  64. package/fixtures/v3/stored/rechnungen.json +1 -1
  65. package/fixtures/v3/v3-vokabular.json +219 -9
  66. package/fixtures/v3/zahlbetrag-faelle.json +1 -1
  67. package/package.json +1 -1
@@ -0,0 +1,242 @@
1
+ import { KasseneckValidationError } from './errors.js';
2
+ import type { InternerDateiOderDatenTransport, InternerTransport } from './aufrufe.js';
3
+ import type { FetchLike } from './transport.js';
4
+ /**
5
+ * DEP-Export fuer die Kassennachschau (nur unter `/v3`): ein Export des
6
+ * Datenerfassungsprotokolls fuer einen vom Pruefungsorgan vorgegebenen
7
+ * Zeitraum (§ 19 Abs. 2 RKSV), Dateien zum Abholen und ein Pruefer-Link mit
8
+ * Code, ueber den das Pruefungsorgan die Dateien ohne Anmeldung laedt.
9
+ *
10
+ * **Anmeldung wie `listReceipts`:** API-Schluessel plus `cashregister-token`;
11
+ * die Kasse kommt aus dem Token, nie aus einem Parameter. Ein Export einer
12
+ * anderen Kasse ist `export_not_found`.
13
+ *
14
+ * **Ablauf:** `createDepExport` stellt den Export ein und antwortet sofort
15
+ * (`status: 'running'`; eine Wiederholung mit derselben `requestId` nennt den
16
+ * aktuellen Stand); er laeuft im Hintergrund. `waitForDepExport` fragt bis zum
17
+ * Ende, `getDepExportFile` holt eine Datei, `createDepHandoverLink` erzeugt den
18
+ * Link fuer das Pruefungsorgan.
19
+ */
20
+ /** Zustand eines Exports: in Arbeit, fertig oder fehlgeschlagen. */
21
+ export declare const DEP_EXPORT_STATUSES: readonly ["running", "done", "failed"];
22
+ export type DepExportStatus = (typeof DEP_EXPORT_STATUSES)[number];
23
+ /** Die Dateien eines Exports: DEP-JSON, Protokoll (PDF), Positionen (CSV) und alles zusammen als ZIP. */
24
+ export declare const DEP_EXPORT_FILES: readonly ["dep", "protocol", "items", "zip"];
25
+ export type DepExportFileKind = (typeof DEP_EXPORT_FILES)[number];
26
+ /**
27
+ * Fehlercodes der fuenf Aufrufe (`code` des [KasseneckApiError]) wie im
28
+ * Vertrag des Backends (`errorCodes.depExport` in `fixtures/v3/v3-vokabular.json`,
29
+ * Katalog `dep_nachschau`), in dessen Reihenfolge; dazu kommen die gemeinsamen
30
+ * Codes von Anmeldung und Rand. Ein Code ausserhalb bleibt ueber
31
+ * `KasseneckApiError.code` lesbar.
32
+ */
33
+ export declare const DEP_EXPORT_ERROR_CODES: readonly ["validation", "invalid_period", "idempotency_conflict", "export_running", "too_many_exports", "export_not_found", "export_not_ready", "export_failed", "invalid_file", "file_not_available", "too_many_links", "link_not_found", "manager_only", "server_error"];
34
+ /**
35
+ * Warum ein Export fehlgeschlagen ist (`DepExport.error`): die Werte des
36
+ * Katalogs `DEP_EXPORT_FEHLER` im Vertrag. `export_stalled` = nach 35 Minuten
37
+ * nicht fertig.
38
+ */
39
+ export declare const DEP_EXPORT_FAILURES: readonly ["export_failed", "export_stalled"];
40
+ export type DepExportFailure = (typeof DEP_EXPORT_FAILURES)[number];
41
+ /**
42
+ * Codes, die die DEP-Aufrufe als `KasseneckApiError.code` werfen:
43
+ * [DEP_EXPORT_ERROR_CODES] und, aus [waitForDepExport], die Gruende eines
44
+ * fehlgeschlagenen Exports ([DEP_EXPORT_FAILURES], also auch `export_stalled`).
45
+ */
46
+ export type DepExportErrorCode = (typeof DEP_EXPORT_ERROR_CODES)[number] | DepExportFailure;
47
+ /** Eingabe von [createDepExport]. Ohne `from`/`to` der ganze Bestand. */
48
+ export interface CreateDepExportOptions {
49
+ /** Erster Tag (inklusive), `YYYY-MM-DD`, nicht in der Zukunft. */
50
+ from?: string;
51
+ /** Letzter Tag (inklusive), `YYYY-MM-DD`, nicht vor `from`. */
52
+ to?: string;
53
+ /**
54
+ * Wiederholungsschutz je Kasse: derselbe Wert mit demselben Zeitraum
55
+ * liefert denselben Export (mit seinem aktuellen Stand) statt eines zweiten;
56
+ * mit anderem Zeitraum `idempotency_conflict`. Hoechstens 64 Zeichen aus
57
+ * `A-Z a-z 0-9 _ -`. Bei unklarem Ausgang (Zeitlimit) mit **demselben** Wert
58
+ * und demselben Zeitraum wiederholen.
59
+ */
60
+ requestId?: string;
61
+ }
62
+ /** Antwort von [createDepExport]: Kennung und Zustand (bei einer wiederholten `requestId` auch `done`). */
63
+ export interface CreateDepExportResult {
64
+ exportId: string;
65
+ status: DepExportStatus;
66
+ from: string | null;
67
+ to: string | null;
68
+ }
69
+ /** Eine Luecke in der Belegnummerierung: Zaehler von bis (einschliesslich). */
70
+ export interface DepExportGap {
71
+ from: number;
72
+ to: number;
73
+ }
74
+ /** Erster Bruch der Signaturkette: am Zaehler `counter` erwartet, aber gefunden. */
75
+ export interface DepExportChainBreak {
76
+ counter: number | null;
77
+ expected: unknown;
78
+ found: unknown;
79
+ }
80
+ /** Befund der Pruefung des Exports (`DepExport.findings`). */
81
+ export interface DepExportFindings {
82
+ receiptCount: number | null;
83
+ counterFrom: number | null;
84
+ counterTo: number | null;
85
+ /** Fehlende Belegnummern als zusammenhaengende Bereiche. */
86
+ gaps: DepExportGap[];
87
+ /** Hoechstens ein Eintrag: der erste Bruch der Kette. */
88
+ chainBreaks: DepExportChainBreak[];
89
+ /** `receiptId` der Belege ohne Zaehler. */
90
+ withoutCounter: string[];
91
+ /** `receiptId` der Belege ohne Signatur. */
92
+ withoutSignature: string[];
93
+ }
94
+ /** Die Positionsdatei (CSV) eines Exports; `null` bei Exporten aus der Zeit davor. */
95
+ export interface DepExportItems {
96
+ rowCount: number;
97
+ sha256: string;
98
+ size: number;
99
+ }
100
+ /** Ein Pruefer-Link, wie ihn der Export auffuehrt (ohne Adresse und Code). */
101
+ export interface DepHandoverLinkInfo {
102
+ linkId: string;
103
+ createdAt: string | null;
104
+ expiresAt: string | null;
105
+ revokedAt: string | null;
106
+ firstAccessAt: string | null;
107
+ accessCount: number;
108
+ }
109
+ /** Ein Export mit Zustand, Kennzahlen, Befund und Links (`getDepExport`). Zeiten ISO 8601 (UTC). */
110
+ export interface DepExport {
111
+ exportId: string;
112
+ status: DepExportStatus;
113
+ from: string | null;
114
+ to: string | null;
115
+ createdAt: string | null;
116
+ finishedAt: string | null;
117
+ receiptCount: number | null;
118
+ counterFrom: number | null;
119
+ counterTo: number | null;
120
+ /** SHA-256 (hex) der DEP-JSON-Datei. */
121
+ sha256: string | null;
122
+ /** Groesse der DEP-JSON-Datei in Byte. */
123
+ size: number | null;
124
+ findings: DepExportFindings | null;
125
+ items: DepExportItems | null;
126
+ handoverLinks: DepHandoverLinkInfo[];
127
+ /** Nur bei `status: 'failed'`: `export_stalled` (haengend) oder `export_failed`. */
128
+ error?: DepExportFailure;
129
+ }
130
+ /** Eingabe von [waitForDepExport]. */
131
+ export interface WaitForDepExportOptions {
132
+ exportId: string;
133
+ /** Hoechstdauer in Millisekunden (Vorgabe 600.000 = 10 Minuten). */
134
+ timeoutMs?: number;
135
+ }
136
+ /** Eingabe von [getDepExportFile]. */
137
+ export interface GetDepExportFileOptions {
138
+ exportId: string;
139
+ file: DepExportFileKind;
140
+ /**
141
+ * Bekannte SHA-256-Pruefsumme (hex) der Datei, etwa `DepExport.sha256` fuer
142
+ * `dep` oder `DepExport.items.sha256` fuer `items`. Weicht die geladene Datei
143
+ * ab, wirft der Aufruf. Bei einem Lese-Link prueft der Aufruf ausserdem immer
144
+ * gegen die Pruefsumme, die der Server mit dem Link nennt.
145
+ */
146
+ sha256?: string;
147
+ /**
148
+ * Zeitlimit in Millisekunden (Vorgabe 600.000), je fuer den Aufruf selbst und
149
+ * fuer das Laden ueber einen Lese-Link. Der Aufruf liefert eine Datei bis
150
+ * 9 MiB direkt und braucht dafuer mehr als das allgemeine Zeitlimit des
151
+ * Transports (`timeoutMs`, Vorgabe 30 s); es gilt hier nicht.
152
+ */
153
+ downloadTimeoutMs?: number;
154
+ }
155
+ /** Eine geladene, gepruefte Datei. */
156
+ export interface DepExportFile {
157
+ bytes: Uint8Array;
158
+ fileName: string;
159
+ contentType: string;
160
+ /** SHA-256 (hex) der Bytes, vom Paket berechnet. */
161
+ sha256: string;
162
+ }
163
+ /** Ein erzeugter Pruefer-Link (`createDepHandoverLink`). Adresse und Code nur jetzt, danach nie wieder. */
164
+ export interface DepHandoverLink {
165
+ linkId: string;
166
+ /** Adresse fuer das Pruefungsorgan (Token im Fragment). */
167
+ url: string;
168
+ /** 6-stelliger Code, getrennt von der Adresse zu uebergeben. */
169
+ code: string;
170
+ expiresAt: string;
171
+ }
172
+ /** Antwort von [revokeDepHandoverLink]. */
173
+ export interface RevokedDepHandoverLink {
174
+ linkId: string;
175
+ revokedAt: string | null;
176
+ }
177
+ /** Wartezeiten von [waitForDepExport]: 2 s, dann je x1,5, hoechstens 15 s. */
178
+ export declare const DEP_EXPORT_POLL_START_MS = 2000;
179
+ export declare const DEP_EXPORT_POLL_FACTOR = 1.5;
180
+ export declare const DEP_EXPORT_POLL_MAX_MS = 15000;
181
+ /** Vorgabe fuer `waitForDepExport.timeoutMs`. */
182
+ export declare const DEP_EXPORT_WAIT_TIMEOUT_MS = 600000;
183
+ /** Vorgabe fuer `getDepExportFile.downloadTimeoutMs`: der Aufruf selbst und das Laden ueber einen Lese-Link. */
184
+ export declare const DEP_EXPORT_DOWNLOAD_TIMEOUT_MS = 600000;
185
+ /**
186
+ * [waitForDepExport] gab auf, weil der Export nach `timeoutMs` weder fertig
187
+ * noch fehlgeschlagen war. Der Export laeuft serverseitig weiter: mit
188
+ * `getDepExport` (oder erneutem Warten) nachsehen, nicht neu anstossen.
189
+ */
190
+ export declare class DepExportTimeoutError extends Error {
191
+ readonly name = "DepExportTimeoutError";
192
+ readonly exportId: string;
193
+ readonly timeoutMs: number;
194
+ /** Zustand bei der letzten Abfrage. */
195
+ readonly lastStatus: DepExportStatus;
196
+ constructor(exportId: string, timeoutMs: number, lastStatus: DepExportStatus);
197
+ }
198
+ /** Das Ergebnis stimmt nicht mit der Pruefsumme ueberein: die Datei ist nicht zu verwenden. */
199
+ export declare class DepExportChecksumError extends KasseneckValidationError {
200
+ readonly expected: string;
201
+ readonly actual: string;
202
+ constructor(exportId: string, file: DepExportFileKind, expected: string, actual: string);
203
+ }
204
+ /**
205
+ * Stellt einen DEP-Export ein (`createDepExport`) und antwortet sofort mit
206
+ * `status: 'running'`. Hoechstens ein laufender Export je Kasse
207
+ * (`export_running`, `details.exportId` nennt ihn). Mit `requestId` ist der
208
+ * Aufruf wiederholbar: dieselbe Kennung mit demselben Zeitraum liefert den
209
+ * bestehenden Export mit seinem aktuellen Stand (auch `done` oder `failed`),
210
+ * mit anderem Zeitraum `idempotency_conflict`.
211
+ */
212
+ export declare function createDepExport(transport: InternerTransport, options?: CreateDepExportOptions): Promise<CreateDepExportResult>;
213
+ /** Zustand, Kennzahlen, Befund und Links eines Exports (`getDepExport`). */
214
+ export declare function getDepExport(transport: InternerTransport, options: {
215
+ exportId: string;
216
+ }): Promise<DepExport>;
217
+ /**
218
+ * Wartet, bis der Export fertig ist, und gibt ihn zurueck. Fragt mit
219
+ * [DEP_EXPORT_POLL_START_MS] Abstand, dann je x[DEP_EXPORT_POLL_FACTOR],
220
+ * hoechstens [DEP_EXPORT_POLL_MAX_MS]. Bei `failed` wirft er einen
221
+ * [KasseneckApiError] mit `code` `export_failed` oder `export_stalled`
222
+ * (haengender Export); nach `timeoutMs` einen [DepExportTimeoutError]. Ein
223
+ * Fehler beim Abfragen selbst bricht ab.
224
+ */
225
+ export declare function waitForDepExport(transport: InternerTransport, options: WaitForDepExportOptions): Promise<DepExport>;
226
+ /**
227
+ * Holt eine Datei eines fertigen Exports (`getDepExportFile`). Der Server
228
+ * liefert sie bis 9 MiB selbst, darueber als signierten Lese-Link (15 Minuten);
229
+ * dem Link folgt dieser Aufruf und laedt die Bytes. In beiden Faellen ist das
230
+ * Ergebnis dasselbe. Geprueft wird die SHA-256-Summe: beim Lese-Link immer
231
+ * gegen die Summe des Servers, sonst und zusaetzlich gegen `options.sha256`,
232
+ * wenn sie bekannt ist. Eine Abweichung wirft [DepExportChecksumError].
233
+ */
234
+ export declare function getDepExportFile(transport: InternerDateiOderDatenTransport, fetchFn: FetchLike, options: GetDepExportFileOptions): Promise<DepExportFile>;
235
+ /** Erzeugt einen Pruefer-Link zu einem fertigen Export (`createDepHandoverLink`). Gueltig 14 Tage. */
236
+ export declare function createDepHandoverLink(transport: InternerTransport, options: {
237
+ exportId: string;
238
+ }): Promise<DepHandoverLink>;
239
+ /** Widerruft einen Pruefer-Link der eigenen Kasse (`revokeDepHandoverLink`). */
240
+ export declare function revokeDepHandoverLink(transport: InternerTransport, options: {
241
+ linkId: string;
242
+ }): Promise<RevokedDepHandoverLink>;
@@ -0,0 +1,326 @@
1
+ import { KasseneckApiError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError } from './errors.js';
2
+ /**
3
+ * DEP-Export fuer die Kassennachschau (nur unter `/v3`): ein Export des
4
+ * Datenerfassungsprotokolls fuer einen vom Pruefungsorgan vorgegebenen
5
+ * Zeitraum (§ 19 Abs. 2 RKSV), Dateien zum Abholen und ein Pruefer-Link mit
6
+ * Code, ueber den das Pruefungsorgan die Dateien ohne Anmeldung laedt.
7
+ *
8
+ * **Anmeldung wie `listReceipts`:** API-Schluessel plus `cashregister-token`;
9
+ * die Kasse kommt aus dem Token, nie aus einem Parameter. Ein Export einer
10
+ * anderen Kasse ist `export_not_found`.
11
+ *
12
+ * **Ablauf:** `createDepExport` stellt den Export ein und antwortet sofort
13
+ * (`status: 'running'`; eine Wiederholung mit derselben `requestId` nennt den
14
+ * aktuellen Stand); er laeuft im Hintergrund. `waitForDepExport` fragt bis zum
15
+ * Ende, `getDepExportFile` holt eine Datei, `createDepHandoverLink` erzeugt den
16
+ * Link fuer das Pruefungsorgan.
17
+ */
18
+ /** Zustand eines Exports: in Arbeit, fertig oder fehlgeschlagen. */
19
+ export const DEP_EXPORT_STATUSES = Object.freeze(['running', 'done', 'failed']);
20
+ /** Die Dateien eines Exports: DEP-JSON, Protokoll (PDF), Positionen (CSV) und alles zusammen als ZIP. */
21
+ export const DEP_EXPORT_FILES = Object.freeze(['dep', 'protocol', 'items', 'zip']);
22
+ /**
23
+ * Fehlercodes der fuenf Aufrufe (`code` des [KasseneckApiError]) wie im
24
+ * Vertrag des Backends (`errorCodes.depExport` in `fixtures/v3/v3-vokabular.json`,
25
+ * Katalog `dep_nachschau`), in dessen Reihenfolge; dazu kommen die gemeinsamen
26
+ * Codes von Anmeldung und Rand. Ein Code ausserhalb bleibt ueber
27
+ * `KasseneckApiError.code` lesbar.
28
+ */
29
+ export const DEP_EXPORT_ERROR_CODES = Object.freeze([
30
+ 'validation', // ungueltige requestId (Details in den Feldfehlern)
31
+ 'invalid_period', // from/to kein Datum JJJJ-MM-TT, from in der Zukunft oder to vor from
32
+ 'idempotency_conflict', // dieselbe requestId gehoert zu einem Export mit anderem Zeitraum
33
+ 'export_running', // fuer diese Kasse laeuft schon ein Export (`details.exportId` nennt ihn)
34
+ 'too_many_exports', // Tagesgrenze der Exporte erreicht
35
+ 'export_not_found', // unbekannt oder einer anderen Kasse
36
+ 'export_not_ready', // Datei oder Link nur zu einem fertigen Export
37
+ 'export_failed', // der Export ist fehlgeschlagen
38
+ 'invalid_file', // keine der Dateien dep, protocol, items, zip
39
+ 'file_not_available', // diese Datei gibt es zu diesem Export nicht (etwa Positionen bei einem alten Export)
40
+ 'too_many_links', // Grenze der aktiven Links je Export oder der neuen Links je Tag erreicht
41
+ 'link_not_found', // unbekannt oder einer anderen Kasse (ein schon widerrufener Link ist kein Fehler: der Widerruf ist wiederholbar)
42
+ 'manager_only', // Kassenweg: nur der Chef (Inhaber oder Recht layout) exportiert; ein Kassier sieht den Hinweis
43
+ 'server_error',
44
+ ]);
45
+ /**
46
+ * Warum ein Export fehlgeschlagen ist (`DepExport.error`): die Werte des
47
+ * Katalogs `DEP_EXPORT_FEHLER` im Vertrag. `export_stalled` = nach 35 Minuten
48
+ * nicht fertig.
49
+ */
50
+ export const DEP_EXPORT_FAILURES = Object.freeze(['export_failed', 'export_stalled']);
51
+ /** Wartezeiten von [waitForDepExport]: 2 s, dann je x1,5, hoechstens 15 s. */
52
+ export const DEP_EXPORT_POLL_START_MS = 2_000;
53
+ export const DEP_EXPORT_POLL_FACTOR = 1.5;
54
+ export const DEP_EXPORT_POLL_MAX_MS = 15_000;
55
+ /** Vorgabe fuer `waitForDepExport.timeoutMs`. */
56
+ export const DEP_EXPORT_WAIT_TIMEOUT_MS = 600_000;
57
+ /** Vorgabe fuer `getDepExportFile.downloadTimeoutMs`: der Aufruf selbst und das Laden ueber einen Lese-Link. */
58
+ export const DEP_EXPORT_DOWNLOAD_TIMEOUT_MS = 600_000;
59
+ /**
60
+ * [waitForDepExport] gab auf, weil der Export nach `timeoutMs` weder fertig
61
+ * noch fehlgeschlagen war. Der Export laeuft serverseitig weiter: mit
62
+ * `getDepExport` (oder erneutem Warten) nachsehen, nicht neu anstossen.
63
+ */
64
+ export class DepExportTimeoutError extends Error {
65
+ name = 'DepExportTimeoutError';
66
+ exportId;
67
+ timeoutMs;
68
+ /** Zustand bei der letzten Abfrage. */
69
+ lastStatus;
70
+ constructor(exportId, timeoutMs, lastStatus) {
71
+ super(`waitForDepExport: Export ${exportId} nach ${timeoutMs} ms nicht fertig (Zustand ${lastStatus})`);
72
+ this.exportId = exportId;
73
+ this.timeoutMs = timeoutMs;
74
+ this.lastStatus = lastStatus;
75
+ }
76
+ }
77
+ /** Das Ergebnis stimmt nicht mit der Pruefsumme ueberein: die Datei ist nicht zu verwenden. */
78
+ export class DepExportChecksumError extends KasseneckValidationError {
79
+ expected;
80
+ actual;
81
+ constructor(exportId, file, expected, actual) {
82
+ super('getDepExportFile', `Pruefsumme der Datei ${file} von ${exportId} stimmt nicht (erwartet ${expected}, berechnet ${actual})`, 'response');
83
+ this.expected = expected;
84
+ this.actual = actual;
85
+ }
86
+ }
87
+ // ---- Hilfen -------------------------------------------------------------------------------------
88
+ const TAG = /^\d{4}-\d{2}-\d{2}$/;
89
+ const ANFRAGE_ID = /^[A-Za-z0-9_-]{1,64}$/;
90
+ const SHA256 = /^[0-9a-f]{64}$/i;
91
+ const VORGABEN = {
92
+ dep: { fileName: (id) => `${id}.json`, contentType: 'application/json' },
93
+ protocol: { fileName: (id) => `${id}.pdf`, contentType: 'application/pdf' },
94
+ items: { fileName: (id) => `${id}_positionen.csv`, contentType: 'text/csv; charset=utf-8' },
95
+ zip: { fileName: (id) => `${id}.zip`, contentType: 'application/zip' },
96
+ };
97
+ function eingabefehler(name, grund) {
98
+ return new KasseneckValidationError(name, grund, 'request');
99
+ }
100
+ function antwortfehler(name, grund) {
101
+ return new KasseneckValidationError(name, grund, 'response');
102
+ }
103
+ const istObjekt = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
104
+ function pruefeKennung(name, feld, wert) {
105
+ if (typeof wert !== 'string' || wert.trim() === '')
106
+ throw eingabefehler(name, `${feld} fehlt oder ist leer`);
107
+ return wert;
108
+ }
109
+ /** Ist das ein echter Kalendertag `JJJJ-MM-TT`? (Rechnung ohne Zeitzone.) */
110
+ function istKalendertag(text) {
111
+ if (!TAG.test(text))
112
+ return false;
113
+ const [j, m, t] = text.split('-').map(Number);
114
+ const d = new Date(Date.UTC(j, m - 1, t));
115
+ return d.getUTCFullYear() === j && d.getUTCMonth() === m - 1 && d.getUTCDate() === t;
116
+ }
117
+ async function sha256Hex(bytes) {
118
+ const umgebung = globalThis;
119
+ const subtle = umgebung.crypto?.subtle;
120
+ if (!subtle)
121
+ throw new Error('getDepExportFile: kein globales crypto.subtle vorhanden — Node >= 20.18 verwenden');
122
+ const digest = new Uint8Array(await subtle.digest('SHA-256', bytes));
123
+ return Array.from(digest, (b) => b.toString(16).padStart(2, '0')).join('');
124
+ }
125
+ function exportAus(daten, name) {
126
+ if (!istObjekt(daten))
127
+ throw antwortfehler(name, 'Antwort enthaelt keine Daten (data fehlt)');
128
+ const status = daten['status'];
129
+ if (typeof daten['exportId'] !== 'string' || !DEP_EXPORT_STATUSES.includes(status)) {
130
+ throw antwortfehler(name, 'Antwort ohne exportId oder mit unbekanntem Zustand');
131
+ }
132
+ if (!Array.isArray(daten['handoverLinks']))
133
+ throw antwortfehler(name, 'Antwort ohne handoverLinks');
134
+ return daten;
135
+ }
136
+ // ---- Aufrufe ------------------------------------------------------------------------------------
137
+ /**
138
+ * Stellt einen DEP-Export ein (`createDepExport`) und antwortet sofort mit
139
+ * `status: 'running'`. Hoechstens ein laufender Export je Kasse
140
+ * (`export_running`, `details.exportId` nennt ihn). Mit `requestId` ist der
141
+ * Aufruf wiederholbar: dieselbe Kennung mit demselben Zeitraum liefert den
142
+ * bestehenden Export mit seinem aktuellen Stand (auch `done` oder `failed`),
143
+ * mit anderem Zeitraum `idempotency_conflict`.
144
+ */
145
+ export async function createDepExport(transport, options = {}) {
146
+ const name = 'createDepExport';
147
+ const params = {};
148
+ for (const feld of ['from', 'to']) {
149
+ const wert = options[feld];
150
+ if (wert === undefined)
151
+ continue;
152
+ if (typeof wert !== 'string' || !istKalendertag(wert))
153
+ throw eingabefehler(name, `${feld} muss ein Datum JJJJ-MM-TT sein`);
154
+ params[feld] = wert;
155
+ }
156
+ if (options.from !== undefined && options.to !== undefined && options.to < options.from) {
157
+ throw eingabefehler(name, 'to liegt vor from');
158
+ }
159
+ if (options.requestId !== undefined) {
160
+ if (typeof options.requestId !== 'string' || !ANFRAGE_ID.test(options.requestId)) {
161
+ throw eingabefehler(name, 'requestId: hoechstens 64 Zeichen aus A-Z, a-z, 0-9, _ und -');
162
+ }
163
+ params['requestId'] = options.requestId;
164
+ }
165
+ const daten = await transport(name, params);
166
+ if (!istObjekt(daten) || typeof daten['exportId'] !== 'string' || !DEP_EXPORT_STATUSES.includes(daten['status'])) {
167
+ throw antwortfehler(name, 'Antwort ohne exportId oder mit unbekanntem Zustand');
168
+ }
169
+ return daten;
170
+ }
171
+ /** Zustand, Kennzahlen, Befund und Links eines Exports (`getDepExport`). */
172
+ export async function getDepExport(transport, options) {
173
+ const name = 'getDepExport';
174
+ const exportId = pruefeKennung(name, 'exportId', options?.exportId);
175
+ return exportAus(await transport(name, { exportId }), name);
176
+ }
177
+ /**
178
+ * Wartet, bis der Export fertig ist, und gibt ihn zurueck. Fragt mit
179
+ * [DEP_EXPORT_POLL_START_MS] Abstand, dann je x[DEP_EXPORT_POLL_FACTOR],
180
+ * hoechstens [DEP_EXPORT_POLL_MAX_MS]. Bei `failed` wirft er einen
181
+ * [KasseneckApiError] mit `code` `export_failed` oder `export_stalled`
182
+ * (haengender Export); nach `timeoutMs` einen [DepExportTimeoutError]. Ein
183
+ * Fehler beim Abfragen selbst bricht ab.
184
+ */
185
+ export async function waitForDepExport(transport, options) {
186
+ const name = 'waitForDepExport';
187
+ const exportId = pruefeKennung(name, 'exportId', options?.exportId);
188
+ const timeoutMs = options.timeoutMs ?? DEP_EXPORT_WAIT_TIMEOUT_MS;
189
+ if (!(typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) && timeoutMs > 0)) {
190
+ throw eingabefehler(name, 'timeoutMs muss eine positive Zahl sein');
191
+ }
192
+ const ende = Date.now() + timeoutMs;
193
+ let abstand = DEP_EXPORT_POLL_START_MS;
194
+ for (;;) {
195
+ const stand = await getDepExport(transport, { exportId });
196
+ if (stand.status === 'done')
197
+ return stand;
198
+ if (stand.status === 'failed') {
199
+ throw new KasseneckApiError('getDepExport', stand.error === 'export_stalled' ? 'Der Export haengt (Stillstand).' : 'Der Export ist fehlgeschlagen.', { exportId, error: stand.error ?? 'export_failed' }, stand.error ?? 'export_failed');
200
+ }
201
+ const rest = ende - Date.now();
202
+ if (rest <= 0)
203
+ throw new DepExportTimeoutError(exportId, timeoutMs, stand.status);
204
+ await new Promise((weiter) => setTimeout(weiter, Math.min(abstand, rest)));
205
+ abstand = Math.min(abstand * DEP_EXPORT_POLL_FACTOR, DEP_EXPORT_POLL_MAX_MS);
206
+ }
207
+ }
208
+ function linkAus(name, wert) {
209
+ if (!istObjekt(wert))
210
+ throw antwortfehler(name, 'Antwort ist weder eine Datei noch ein Lese-Link (data.download fehlt)');
211
+ const { url, expiresAt, size, sha256, fileName, contentType } = wert;
212
+ if (typeof url !== 'string' || typeof expiresAt !== 'string' || typeof fileName !== 'string' || typeof contentType !== 'string' ||
213
+ typeof sha256 !== 'string' || !SHA256.test(sha256) || typeof size !== 'number' || !Number.isSafeInteger(size) || size < 0) {
214
+ throw antwortfehler(name, 'Lese-Link unvollstaendig (url, expiresAt, size, sha256, fileName, contentType)');
215
+ }
216
+ let adresse;
217
+ try {
218
+ adresse = new URL(url);
219
+ }
220
+ catch {
221
+ throw antwortfehler(name, 'Lese-Link ist keine Adresse');
222
+ }
223
+ const lokal = adresse.hostname === 'localhost' || adresse.hostname === '127.0.0.1' || adresse.hostname === '[::1]';
224
+ if (adresse.protocol !== 'https:' && !(adresse.protocol === 'http:' && lokal)) {
225
+ throw antwortfehler(name, 'Lese-Link ist keine https-Adresse');
226
+ }
227
+ return { url, expiresAt, size, sha256: sha256.toLowerCase(), fileName, contentType };
228
+ }
229
+ /** Laedt die Bytes hinter einem Lese-Link. Fehlertexte und Fehlerobjekte nennen die Adresse nie (sie traegt die Berechtigung). */
230
+ async function ladeLink(fetchFn, url, timeoutMs) {
231
+ const name = 'getDepExportFile';
232
+ const abbruch = new AbortController();
233
+ let abgelaufen = false;
234
+ const wecker = setTimeout(() => {
235
+ abgelaufen = true;
236
+ abbruch.abort();
237
+ }, timeoutMs);
238
+ try {
239
+ // Ein GET ohne Rumpf und ohne eigene Kopfzeilen; FetchLike verlangt formal einen Rumpf, den ein GET nicht tragen darf.
240
+ const antwort = await fetchFn(url, { method: 'GET', headers: {}, signal: abbruch.signal });
241
+ if (antwort.status !== 200) {
242
+ throw new KasseneckHttpError(name, antwort.status, antwort.headers.get('content-type') ?? undefined, 'server-error', 'rejected');
243
+ }
244
+ return new Uint8Array(await antwort.arrayBuffer());
245
+ }
246
+ catch (ursache) {
247
+ if (ursache instanceof KasseneckHttpError)
248
+ throw ursache;
249
+ throw new KasseneckNetworkError(name, abgelaufen, timeoutMs, {}, 'rejected');
250
+ }
251
+ finally {
252
+ clearTimeout(wecker);
253
+ }
254
+ }
255
+ /**
256
+ * Holt eine Datei eines fertigen Exports (`getDepExportFile`). Der Server
257
+ * liefert sie bis 9 MiB selbst, darueber als signierten Lese-Link (15 Minuten);
258
+ * dem Link folgt dieser Aufruf und laedt die Bytes. In beiden Faellen ist das
259
+ * Ergebnis dasselbe. Geprueft wird die SHA-256-Summe: beim Lese-Link immer
260
+ * gegen die Summe des Servers, sonst und zusaetzlich gegen `options.sha256`,
261
+ * wenn sie bekannt ist. Eine Abweichung wirft [DepExportChecksumError].
262
+ */
263
+ export async function getDepExportFile(transport, fetchFn, options) {
264
+ const name = 'getDepExportFile';
265
+ const exportId = pruefeKennung(name, 'exportId', options?.exportId);
266
+ if (!DEP_EXPORT_FILES.includes(options.file)) {
267
+ throw eingabefehler(name, 'file muss dep, protocol, items oder zip sein');
268
+ }
269
+ if (options.sha256 !== undefined && !(typeof options.sha256 === 'string' && SHA256.test(options.sha256))) {
270
+ throw eingabefehler(name, 'sha256 muss 64 Hexzeichen haben');
271
+ }
272
+ const frist = options.downloadTimeoutMs ?? DEP_EXPORT_DOWNLOAD_TIMEOUT_MS;
273
+ if (!(typeof frist === 'number' && Number.isFinite(frist) && frist > 0)) {
274
+ throw eingabefehler(name, 'downloadTimeoutMs muss eine positive Zahl sein');
275
+ }
276
+ const bekannt = options.sha256?.toLowerCase();
277
+ const vorgabe = VORGABEN[options.file];
278
+ // Eine direkte Datei (bis 9 MiB) kommt im Aufruf selbst: dieselbe Frist wie das Laden ueber den Link, nicht die 30 s
279
+ // des Transports.
280
+ const ergebnis = await transport(name, { exportId, file: options.file }, { timeoutMs: frist });
281
+ let bytes;
282
+ let fileName;
283
+ let contentType;
284
+ let vomServer;
285
+ if ('file' in ergebnis) {
286
+ bytes = ergebnis.file.bytes;
287
+ fileName = ergebnis.file.fileName ?? vorgabe.fileName(exportId);
288
+ contentType = ergebnis.file.contentType ?? vorgabe.contentType;
289
+ }
290
+ else {
291
+ const link = linkAus(name, istObjekt(ergebnis.data) ? ergebnis.data['download'] : undefined);
292
+ bytes = await ladeLink(fetchFn, link.url, frist);
293
+ if (bytes.length !== link.size) {
294
+ throw antwortfehler(name, `Die geladene Datei hat ${bytes.length} Byte, der Server nannte ${link.size}`);
295
+ }
296
+ fileName = link.fileName;
297
+ contentType = link.contentType;
298
+ vomServer = link.sha256;
299
+ }
300
+ const summe = await sha256Hex(bytes);
301
+ for (const erwartet of [vomServer, bekannt]) {
302
+ if (erwartet !== undefined && erwartet !== summe)
303
+ throw new DepExportChecksumError(exportId, options.file, erwartet, summe);
304
+ }
305
+ return { bytes, fileName, contentType, sha256: summe };
306
+ }
307
+ /** Erzeugt einen Pruefer-Link zu einem fertigen Export (`createDepHandoverLink`). Gueltig 14 Tage. */
308
+ export async function createDepHandoverLink(transport, options) {
309
+ const name = 'createDepHandoverLink';
310
+ const exportId = pruefeKennung(name, 'exportId', options?.exportId);
311
+ const daten = await transport(name, { exportId });
312
+ if (!istObjekt(daten) || typeof daten['linkId'] !== 'string' || typeof daten['url'] !== 'string' ||
313
+ typeof daten['code'] !== 'string' || typeof daten['expiresAt'] !== 'string') {
314
+ throw antwortfehler(name, 'Antwort ohne linkId, url, code oder expiresAt');
315
+ }
316
+ return { linkId: daten['linkId'], url: daten['url'], code: daten['code'], expiresAt: daten['expiresAt'] };
317
+ }
318
+ /** Widerruft einen Pruefer-Link der eigenen Kasse (`revokeDepHandoverLink`). */
319
+ export async function revokeDepHandoverLink(transport, options) {
320
+ const name = 'revokeDepHandoverLink';
321
+ const linkId = pruefeKennung(name, 'linkId', options?.linkId);
322
+ const daten = await transport(name, { linkId });
323
+ if (!istObjekt(daten) || typeof daten['linkId'] !== 'string')
324
+ throw antwortfehler(name, 'Antwort ohne linkId');
325
+ return { linkId: daten['linkId'], revokedAt: typeof daten['revokedAt'] === 'string' ? daten['revokedAt'] : null };
326
+ }
@@ -1,10 +1,11 @@
1
1
  export { type AuthCredentials, type KasseneckAuth, type ApiKeyAuthOptions, type RegisterUserAuthOptions, apiKeyAuth, registerUserAuth, } from './auth.js';
2
- export { DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type TransportCallOptions, type KasseneckTransport, type KasseneckBinaryTransport, type KasseneckPdfOrDataTransport, type PdfOrData, createTransport, createBinaryTransport, createPdfOrDataTransport, } from './transport.js';
2
+ export { DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, type HttpResponseLike, type HttpRequestInit, type FetchLike, type TransportOptions, type TransportBodyFields, type TransportCallOptions, type KasseneckTransport, type KasseneckBinaryTransport, type KasseneckPdfOrDataTransport, type PdfOrData, createTransport, createBinaryTransport, createPdfOrDataTransport, createFileOrDataTransport, type FileOrData, type FileOrDataCallOptions, type KasseneckFileOrDataTransport, } from './transport.js';
3
3
  export { ALL_CALLS, type ApiCall, PUBLIC_CALLS, POS_CALLS, UNKNOWN_OUTCOME_CALLS, type PublicCall, type PosCall, } from './aufrufe.js';
4
4
  export { KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, type ValidationScope, type KasseneckError, type HttpFailureReason, type CauseDigest, type ErrorOutcome, CLIENT_ERROR_CODES, type ClientErrorCode, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, isOutcomeUnknown, } from './errors.js';
5
5
  export { type ReceiptCommonOptions, type SellReceiptOptions, type TipOptions, type TipRecipientShare, type CancelReceiptOptions, type ReceiptWithCompany, sellReceipt, sellReceiptWithCompany, paymentsExpectedCents, cardRefundReference, receiptLayoutFromResult, cancelReceipt, type CancelReceiptResult, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, type ListMyReceiptsOptions, type ReceiptList, type ReceiptListStats, checkVoucherCombinationError, type SendReceiptEmailOptions, type SendReceiptEmailResult, sendReceiptEmail, type ReceiptOutputEntry, type LogReceiptOutputsOptions, type LogReceiptOutputsResult, type RejectedReceiptOutput, logReceiptOutputs, } from './receipts.js';
6
6
  export { listMyCashregisters } from './cashregisters.js';
7
7
  export { listReceipts, listAllReceipts, fromListReceiptsSummaryPayload, listedReceiptWithCompany, LIST_RECEIPTS_VIEWS, LIST_RECEIPTS_ORDERS, LIST_RECEIPTS_LIMIT_MAX, LIST_RECEIPTS_DAYS_MAX, LIST_ALL_RECEIPTS_MAX_PAGES, LIST_RECEIPTS_ERROR_CODES, type ListReceiptsErrorCode, type ListReceiptsView, type ListReceiptsOrder, type ListReceiptsOptions, type ListReceiptsWindow, type ListReceiptsGrossByRate, type ListReceiptsSummary, type ListReceiptsDaySummary, type ListReceiptsPage, type ListedReceipt, type ListedFullReceipt, type ListReceiptsHeader, } from './list-receipts.js';
8
+ export { createDepExport, getDepExport, waitForDepExport, getDepExportFile, createDepHandoverLink, revokeDepHandoverLink, DepExportTimeoutError, DepExportChecksumError, DEP_EXPORT_STATUSES, DEP_EXPORT_FILES, DEP_EXPORT_ERROR_CODES, DEP_EXPORT_FAILURES, DEP_EXPORT_POLL_START_MS, DEP_EXPORT_POLL_FACTOR, DEP_EXPORT_POLL_MAX_MS, DEP_EXPORT_WAIT_TIMEOUT_MS, DEP_EXPORT_DOWNLOAD_TIMEOUT_MS, type DepExportStatus, type DepExportFileKind, type DepExportErrorCode, type DepExportFailure, type CreateDepExportOptions, type CreateDepExportResult, type DepExportGap, type DepExportChainBreak, type DepExportFindings, type DepExportItems, type DepHandoverLinkInfo, type DepExport, type WaitForDepExportOptions, type GetDepExportFileOptions, type DepExportFile, type DepHandoverLink, type RevokedDepHandoverLink, } from './dep-export.js';
8
9
  export { downloadDailyReport, downloadMonthlyReport, getCashregisterReport, getReportV2, type CashregisterReportPeriod, type ReportV2, type ReportV2Metadata, type ReportV2Options } from './reports.js';
9
10
  export { type CashboxStatus, type SignatureStatus, getCashboxStatus, getSignatureStatus, } from './status.js';
10
11
  export { type KasseneckApi, createKasseneckApi } from './api.js';
@@ -1,10 +1,11 @@
1
1
  export { apiKeyAuth, registerUserAuth, } from './auth.js';
2
- export { DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, createTransport, createBinaryTransport, createPdfOrDataTransport, } from './transport.js';
2
+ export { DEFAULT_BASE_URL, POS_BASE_URL, DEFAULT_TIMEOUT_MS, createTransport, createBinaryTransport, createPdfOrDataTransport, createFileOrDataTransport, } from './transport.js';
3
3
  export { ALL_CALLS, PUBLIC_CALLS, POS_CALLS, UNKNOWN_OUTCOME_CALLS, } from './aufrufe.js';
4
4
  export { KasseneckApiError, KasseneckAuthError, KasseneckHttpError, KasseneckNetworkError, KasseneckValidationError, CLIENT_ERROR_CODES, isKasseneckApiError, isKasseneckAuthError, isKasseneckHttpError, isKasseneckNetworkError, isKasseneckValidationError, isOutcomeUnknown, } from './errors.js';
5
5
  export { sellReceipt, sellReceiptWithCompany, paymentsExpectedCents, cardRefundReference, receiptLayoutFromResult, cancelReceipt, zeroReceipt, getReceipt, getReceiptWithCompany, generateFullReceiptId, getFirstReceiptDate, listMyReceipts, checkVoucherCombinationError, sendReceiptEmail, logReceiptOutputs, } from './receipts.js';
6
6
  export { listMyCashregisters } from './cashregisters.js';
7
7
  export { listReceipts, listAllReceipts, fromListReceiptsSummaryPayload, listedReceiptWithCompany, LIST_RECEIPTS_VIEWS, LIST_RECEIPTS_ORDERS, LIST_RECEIPTS_LIMIT_MAX, LIST_RECEIPTS_DAYS_MAX, LIST_ALL_RECEIPTS_MAX_PAGES, LIST_RECEIPTS_ERROR_CODES, } from './list-receipts.js';
8
+ export { createDepExport, getDepExport, waitForDepExport, getDepExportFile, createDepHandoverLink, revokeDepHandoverLink, DepExportTimeoutError, DepExportChecksumError, DEP_EXPORT_STATUSES, DEP_EXPORT_FILES, DEP_EXPORT_ERROR_CODES, DEP_EXPORT_FAILURES, DEP_EXPORT_POLL_START_MS, DEP_EXPORT_POLL_FACTOR, DEP_EXPORT_POLL_MAX_MS, DEP_EXPORT_WAIT_TIMEOUT_MS, DEP_EXPORT_DOWNLOAD_TIMEOUT_MS, } from './dep-export.js';
8
9
  export { downloadDailyReport, downloadMonthlyReport, getCashregisterReport, getReportV2 } from './reports.js';
9
10
  export { getCashboxStatus, getSignatureStatus, } from './status.js';
10
11
  export { createKasseneckApi } from './api.js';
@@ -91,16 +91,16 @@ export interface TransportOptions {
91
91
  auth: KasseneckAuth;
92
92
  /**
93
93
  * Abweichende Basis der **oeffentlichen** Aufrufe (Vorgabe
94
- * [DEFAULT_BASE_URL]): alles ausser den 33 Aufrufen des Kassenwegs, und die
95
- * sechs oeffentlichen davon nur, wenn nicht mit `registerUserAuth`
94
+ * [DEFAULT_BASE_URL]): alles ausser den 47 Aufrufen des Kassenwegs, und die
95
+ * elf oeffentlichen davon nur, wenn nicht mit `registerUserAuth`
96
96
  * angemeldet. Muss auf `/v3` enden (eigene Proxys erlaubt), sonst wirft das
97
97
  * Anlegen; `/v1` oder `/api` gibt es in der 1.x-Linie nicht.
98
98
  */
99
99
  baseUrl?: string;
100
100
  /**
101
- * Abweichende Basis des **Kassenwegs** (Vorgabe [POS_BASE_URL]): die 27
101
+ * Abweichende Basis des **Kassenwegs** (Vorgabe [POS_BASE_URL]): die 36
102
102
  * reinen Kassenaufrufe (Kopplung, Anmeldung, Einstellungen, Artikel,
103
- * Drucker, ...) und mit `registerUserAuth` alle 33 Aufrufe des Kassenwegs.
103
+ * Drucker, ...) und mit `registerUserAuth` alle 47 Aufrufe des Kassenwegs.
104
104
  * Die Web-Kasse gibt `'/api/v3'` (gleicher Ursprung). Muss auf `/v3` enden
105
105
  * (in der Regel `/api/v3`), sonst wirft das Anlegen.
106
106
  */
@@ -217,3 +217,38 @@ export type KasseneckPdfOrDataTransport = (functionName: string, params?: Record
217
217
  * ist unlesbar mit denselben Gruenden wie dort.
218
218
  */
219
219
  export declare function createPdfOrDataTransport(options: TransportOptions): KasseneckPdfOrDataTransport;
220
+ /**
221
+ * Ergebnis eines Aufrufs, der eine beliebige Datei **oder** eine Nutzlast
222
+ * liefert: die Datei mit Namen und Inhaltstyp aus den Kopfzeilen der Antwort
223
+ * (`Content-Disposition: attachment`), oder die `data` einer Erfolgshuelle
224
+ * (etwa ein Lese-Link, wenn die Datei zu gross fuer die Antwort ist).
225
+ */
226
+ export type FileOrData = {
227
+ file: {
228
+ bytes: Uint8Array;
229
+ fileName: string | null;
230
+ contentType: string | undefined;
231
+ };
232
+ } | {
233
+ data: unknown;
234
+ };
235
+ /** Optionen je Aufruf des Datei-oder-Daten-Wegs ([createFileOrDataTransport]). */
236
+ export interface FileOrDataCallOptions {
237
+ /**
238
+ * Zeitlimit dieses Aufrufs in Millisekunden (Anmeldung, Anfrage und Lesen
239
+ * des Rumpfs); ohne Angabe `TransportOptions.timeoutMs` (Vorgabe 30 s). Eine
240
+ * Datei kommt im Rumpf und braucht dafuer laenger als eine Huelle.
241
+ */
242
+ timeoutMs?: number;
243
+ }
244
+ /** Ruft eine Backend-Funktion auf, die irgendeine Datei **oder** eine JSON-Nutzlast liefert. */
245
+ export type KasseneckFileOrDataTransport = (functionName: string, params?: Record<string, unknown>, options?: FileOrDataCallOptions) => Promise<FileOrData>;
246
+ /**
247
+ * Vierter Einstiegspunkt, fuer Aufrufe, deren Datei kein PDF ist (DEP-JSON,
248
+ * CSV, ZIP: `getDepExportFile`). Die Antwort wird nur als Bytes gelesen.
249
+ * Entschieden wird an der Kopfzeile `Content-Disposition: attachment` — eine
250
+ * Datei ist hier selbst JSON und liesse sich am Inhalt nicht von der
251
+ * Erfolgshuelle trennen. Ohne diese Kopfzeile gilt der Rumpf als Huelle
252
+ * (Erfolg: `{ data }`, Fehler: derselbe fachliche Fehler wie auf dem JSON-Weg).
253
+ */
254
+ export declare function createFileOrDataTransport(options: TransportOptions): KasseneckFileOrDataTransport;