@ripwords/myinvois-client 0.2.41 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/documentManagement.d.ts +2 -2
- package/dist/api/documentSubmission.d.ts +2 -2
- package/dist/api/documentSubmission.js +2 -2
- package/dist/api/documentTypeManagement.d.ts +2 -2
- package/dist/api/notificationManagement.d.ts +2 -2
- package/dist/api/platformLogin.d.ts +2 -2
- package/dist/api/taxpayerValidation.d.ts +2 -2
- package/dist/apiQueue-B6Q644Bz.js +201 -0
- package/dist/apiQueue-DgKWaQDS.cjs +220 -0
- package/dist/apiQueue-DgKWaQDS.cjs.map +1 -0
- package/dist/{document-DLFdGSK1.js → document-D4O7JY0G.js} +9 -3
- package/dist/{document-CCza2JPL.cjs → document-DoQEvmcK.cjs} +10 -4
- package/dist/document-DoQEvmcK.cjs.map +1 -0
- package/dist/{documentSubmission-M4UlirJ7.cjs → documentSubmission-DUsjqWhR.cjs} +2 -2
- package/dist/{documentSubmission-M4UlirJ7.cjs.map → documentSubmission-DUsjqWhR.cjs.map} +1 -1
- package/dist/{documentSubmission-ZAgXsd3X.js → documentSubmission-uJ7yPWub.js} +1 -1
- package/dist/{documents-DCZ3Ffya.d.cts → documents-BECak3KN.d.cts} +7 -1
- package/dist/{documents-DzZA3NHj.d.ts → documents-Dp19RgNX.d.ts} +6 -0
- package/dist/index.cjs +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/index10.cjs +24 -4
- package/dist/index10.cjs.map +1 -0
- package/dist/index11.cjs +0 -22
- package/dist/index12.cjs +33 -2
- package/dist/index12.cjs.map +1 -0
- package/dist/index13.cjs +23 -2
- package/dist/index13.cjs.map +1 -0
- package/dist/index14.cjs +0 -330
- package/dist/index15.cjs +0 -193
- package/dist/index16.cjs +0 -62
- package/dist/index17.cjs +4 -531
- package/dist/index18.cjs +6 -195
- package/dist/index19.cjs +5 -0
- package/dist/index2.cjs +61 -4
- package/dist/index2.cjs.map +1 -0
- package/dist/index20.cjs +2 -24
- package/dist/index21.cjs +3 -0
- package/dist/index22.cjs +6 -0
- package/dist/index23.cjs +203 -24
- package/dist/index23.cjs.map +1 -1
- package/dist/index24.cjs +104 -20
- package/dist/index24.cjs.map +1 -1
- package/dist/index25.cjs +137 -0
- package/dist/{index33.cjs.map → index25.cjs.map} +1 -1
- package/dist/index26.cjs +59 -29
- package/dist/index26.cjs.map +1 -1
- package/dist/index27.cjs +262 -19
- package/dist/index27.cjs.map +1 -1
- package/dist/index28.cjs +79 -0
- package/dist/{index36.cjs.map → index28.cjs.map} +1 -1
- package/dist/index29.cjs +107 -0
- package/dist/{index37.cjs.map → index29.cjs.map} +1 -1
- package/dist/index3.cjs +531 -6
- package/dist/index3.cjs.map +1 -0
- package/dist/index30.cjs +73 -0
- package/dist/{index38.cjs.map → index30.cjs.map} +1 -1
- package/dist/index31.cjs +107 -203
- package/dist/index31.cjs.map +1 -1
- package/dist/index32.cjs +95 -104
- package/dist/index32.cjs.map +1 -1
- package/dist/index33.cjs +4 -136
- package/dist/index34.cjs +9 -60
- package/dist/index34.cjs.map +1 -1
- package/dist/index35.cjs +4 -266
- package/dist/index36.cjs +21 -78
- package/dist/index37.cjs +2 -106
- package/dist/index38.cjs +2 -72
- package/dist/index39.cjs +326 -108
- package/dist/index39.cjs.map +1 -1
- package/dist/index4.cjs +195 -4
- package/dist/index4.cjs.map +1 -0
- package/dist/index40.cjs +189 -96
- package/dist/index40.cjs.map +1 -1
- package/dist/index5.cjs +0 -3
- package/dist/index6.cjs +24 -2
- package/dist/index6.cjs.map +1 -0
- package/dist/index68.cts.map +1 -1
- package/dist/index7.cjs +0 -6
- package/dist/index71.cts.map +1 -1
- package/dist/index8.cjs +0 -4
- package/dist/index9.cjs +25 -9
- package/dist/index9.cjs.map +1 -1
- package/dist/{taxpayer-DmHW0m7o.d.ts → taxpayer-BdvCGHHC.d.ts} +1 -1
- package/dist/{taxpayer-Pm90MrPj.d.cts → taxpayer-CaDfslWB.d.cts} +2 -2
- package/dist/types/documents.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/taxpayer.d.ts +2 -2
- package/dist/utils/apiQueue.d.ts +11 -3
- package/dist/utils/apiQueue.js +2 -2
- package/dist/utils/document.d.ts +4 -3
- package/dist/utils/document.js +1 -1
- package/dist/utils/signature-diagnostics.d.ts +2 -2
- package/dist/utils/signature-diagnostics.js +1 -1
- package/dist/utils/validation.d.ts +2 -2
- package/package.json +1 -1
- package/dist/apiQueue-CCrZMnMu.js +0 -182
- package/dist/apiQueue-Djd7WlnV.cjs +0 -195
- package/dist/apiQueue-Djd7WlnV.cjs.map +0 -1
- package/dist/document-CCza2JPL.cjs.map +0 -1
- package/dist/index14.cjs.map +0 -1
- package/dist/index15.cjs.map +0 -1
- package/dist/index16.cjs.map +0 -1
- package/dist/index17.cjs.map +0 -1
- package/dist/index18.cjs.map +0 -1
- package/dist/index20.cjs.map +0 -1
- package/dist/index35.cjs.map +0 -1
|
@@ -51,11 +51,11 @@ import "../YX-F34sJ7Ik.js";
|
|
|
51
51
|
import "../ZX-CDQOfsHh.js";
|
|
52
52
|
import "../XX-DOA-10JW.js";
|
|
53
53
|
import "../unit-types-VgYXIwTT.js";
|
|
54
|
-
import { DocumentStatus, DocumentSummary, DocumentValidationResult, DocumentValidationStepResult } from "../documents-
|
|
54
|
+
import { DocumentStatus, DocumentSummary, DocumentValidationResult, DocumentValidationStepResult } from "../documents-Dp19RgNX.js";
|
|
55
55
|
import "../payment-modes-g3DzLmWb.js";
|
|
56
56
|
import "../signatures-CerHUrj3.js";
|
|
57
57
|
import "../notifications-sFhgh3rJ.js";
|
|
58
|
-
import "../taxpayer-
|
|
58
|
+
import "../taxpayer-BdvCGHHC.js";
|
|
59
59
|
import { Fetch } from "../utils-C4FoVKLq.js";
|
|
60
60
|
import "../index-CygwSf0x.js";
|
|
61
61
|
|
|
@@ -51,11 +51,11 @@ import "../YX-F34sJ7Ik.js";
|
|
|
51
51
|
import "../ZX-CDQOfsHh.js";
|
|
52
52
|
import "../XX-DOA-10JW.js";
|
|
53
53
|
import "../unit-types-VgYXIwTT.js";
|
|
54
|
-
import { AllDocumentsV1_1, DocumentSummary, SigningCredentials, StandardError, SubmissionResponse, SubmissionStatus } from "../documents-
|
|
54
|
+
import { AllDocumentsV1_1, DocumentSummary, SigningCredentials, StandardError, SubmissionResponse, SubmissionStatus } from "../documents-Dp19RgNX.js";
|
|
55
55
|
import "../payment-modes-g3DzLmWb.js";
|
|
56
56
|
import "../signatures-CerHUrj3.js";
|
|
57
57
|
import "../notifications-sFhgh3rJ.js";
|
|
58
|
-
import "../taxpayer-
|
|
58
|
+
import "../taxpayer-BdvCGHHC.js";
|
|
59
59
|
import { Fetch } from "../utils-C4FoVKLq.js";
|
|
60
60
|
import "../index-CygwSf0x.js";
|
|
61
61
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../formatIdValue-qTxJqj9o.js";
|
|
2
|
-
import "../document-
|
|
3
|
-
import { getSubmissionStatus, performDocumentAction, submitDocument } from "../documentSubmission-
|
|
2
|
+
import "../document-D4O7JY0G.js";
|
|
3
|
+
import { getSubmissionStatus, performDocumentAction, submitDocument } from "../documentSubmission-uJ7yPWub.js";
|
|
4
4
|
|
|
5
5
|
export { getSubmissionStatus, performDocumentAction, submitDocument };
|
|
@@ -51,11 +51,11 @@ import "../YX-F34sJ7Ik.js";
|
|
|
51
51
|
import "../ZX-CDQOfsHh.js";
|
|
52
52
|
import "../XX-DOA-10JW.js";
|
|
53
53
|
import "../unit-types-VgYXIwTT.js";
|
|
54
|
-
import { DocumentTypeResponse, DocumentTypeVersionResponse, DocumentTypesResponse } from "../documents-
|
|
54
|
+
import { DocumentTypeResponse, DocumentTypeVersionResponse, DocumentTypesResponse } from "../documents-Dp19RgNX.js";
|
|
55
55
|
import "../payment-modes-g3DzLmWb.js";
|
|
56
56
|
import "../signatures-CerHUrj3.js";
|
|
57
57
|
import "../notifications-sFhgh3rJ.js";
|
|
58
|
-
import "../taxpayer-
|
|
58
|
+
import "../taxpayer-BdvCGHHC.js";
|
|
59
59
|
import { Fetch } from "../utils-C4FoVKLq.js";
|
|
60
60
|
import "../index-CygwSf0x.js";
|
|
61
61
|
|
|
@@ -51,11 +51,11 @@ import "../YX-F34sJ7Ik.js";
|
|
|
51
51
|
import "../ZX-CDQOfsHh.js";
|
|
52
52
|
import "../XX-DOA-10JW.js";
|
|
53
53
|
import "../unit-types-VgYXIwTT.js";
|
|
54
|
-
import "../documents-
|
|
54
|
+
import "../documents-Dp19RgNX.js";
|
|
55
55
|
import "../payment-modes-g3DzLmWb.js";
|
|
56
56
|
import "../signatures-CerHUrj3.js";
|
|
57
57
|
import { NotificationResponse, NotificationSearchParams } from "../notifications-sFhgh3rJ.js";
|
|
58
|
-
import "../taxpayer-
|
|
58
|
+
import "../taxpayer-BdvCGHHC.js";
|
|
59
59
|
import { Fetch } from "../utils-C4FoVKLq.js";
|
|
60
60
|
import "../index-CygwSf0x.js";
|
|
61
61
|
|
|
@@ -51,11 +51,11 @@ import "../YX-F34sJ7Ik.js";
|
|
|
51
51
|
import "../ZX-CDQOfsHh.js";
|
|
52
52
|
import "../XX-DOA-10JW.js";
|
|
53
53
|
import "../unit-types-VgYXIwTT.js";
|
|
54
|
-
import "../documents-
|
|
54
|
+
import "../documents-Dp19RgNX.js";
|
|
55
55
|
import "../payment-modes-g3DzLmWb.js";
|
|
56
56
|
import "../signatures-CerHUrj3.js";
|
|
57
57
|
import "../notifications-sFhgh3rJ.js";
|
|
58
|
-
import "../taxpayer-
|
|
58
|
+
import "../taxpayer-BdvCGHHC.js";
|
|
59
59
|
import "../utils-C4FoVKLq.js";
|
|
60
60
|
import { ClientCredentials } from "../index-CygwSf0x.js";
|
|
61
61
|
|
|
@@ -51,11 +51,11 @@ import "../YX-F34sJ7Ik.js";
|
|
|
51
51
|
import "../ZX-CDQOfsHh.js";
|
|
52
52
|
import "../XX-DOA-10JW.js";
|
|
53
53
|
import "../unit-types-VgYXIwTT.js";
|
|
54
|
-
import { RegistrationType } from "../documents-
|
|
54
|
+
import { RegistrationType } from "../documents-Dp19RgNX.js";
|
|
55
55
|
import "../payment-modes-g3DzLmWb.js";
|
|
56
56
|
import "../signatures-CerHUrj3.js";
|
|
57
57
|
import "../notifications-sFhgh3rJ.js";
|
|
58
|
-
import { TaxpayerQRCodeResponse, TinSearchParams, TinSearchResponse } from "../taxpayer-
|
|
58
|
+
import { TaxpayerQRCodeResponse, TinSearchParams, TinSearchResponse } from "../taxpayer-BdvCGHHC.js";
|
|
59
59
|
import { Fetch } from "../utils-C4FoVKLq.js";
|
|
60
60
|
import "../index-CygwSf0x.js";
|
|
61
61
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
//#region src/utils/apiQueue.ts
|
|
2
|
+
const queues = {};
|
|
3
|
+
const LIMITS = {
|
|
4
|
+
loginTaxpayer: {
|
|
5
|
+
max: 12,
|
|
6
|
+
perMs: 6e4
|
|
7
|
+
},
|
|
8
|
+
loginIntermediary: {
|
|
9
|
+
max: 12,
|
|
10
|
+
perMs: 6e4
|
|
11
|
+
},
|
|
12
|
+
submitDocuments: {
|
|
13
|
+
max: 100,
|
|
14
|
+
perMs: 6e4
|
|
15
|
+
},
|
|
16
|
+
getSubmission: {
|
|
17
|
+
max: 300,
|
|
18
|
+
perMs: 6e4
|
|
19
|
+
},
|
|
20
|
+
cancelDocument: {
|
|
21
|
+
max: 12,
|
|
22
|
+
perMs: 6e4
|
|
23
|
+
},
|
|
24
|
+
rejectDocument: {
|
|
25
|
+
max: 12,
|
|
26
|
+
perMs: 6e4
|
|
27
|
+
},
|
|
28
|
+
getDocument: {
|
|
29
|
+
max: 60,
|
|
30
|
+
perMs: 6e4
|
|
31
|
+
},
|
|
32
|
+
getDocumentDetails: {
|
|
33
|
+
max: 125,
|
|
34
|
+
perMs: 6e4
|
|
35
|
+
},
|
|
36
|
+
getRecentDocuments: {
|
|
37
|
+
max: 12,
|
|
38
|
+
perMs: 6e4
|
|
39
|
+
},
|
|
40
|
+
searchDocuments: {
|
|
41
|
+
max: 12,
|
|
42
|
+
perMs: 6e4
|
|
43
|
+
},
|
|
44
|
+
searchTin: {
|
|
45
|
+
max: 60,
|
|
46
|
+
perMs: 6e4
|
|
47
|
+
},
|
|
48
|
+
taxpayerQr: {
|
|
49
|
+
max: 60,
|
|
50
|
+
perMs: 6e4
|
|
51
|
+
},
|
|
52
|
+
default: {
|
|
53
|
+
max: 12,
|
|
54
|
+
perMs: 6e4
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Clean up old timestamps outside the sliding window
|
|
59
|
+
*/
|
|
60
|
+
function cleanupTimestamps(timestamps, windowMs, now) {
|
|
61
|
+
return timestamps.filter((ts) => now - ts < windowMs);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Calculate when we can make the next request without exceeding the rate limit
|
|
65
|
+
*/
|
|
66
|
+
function getNextAvailableTime(timestamps, max, windowMs, now) {
|
|
67
|
+
if (timestamps.length < max) return now;
|
|
68
|
+
const oldestTimestamp = timestamps[0];
|
|
69
|
+
if (!oldestTimestamp) return now;
|
|
70
|
+
return oldestTimestamp + windowMs;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Public helper to schedule a request according to the category's limits.
|
|
74
|
+
* Rate limits are enforced per clientId, so multiple instances with the same
|
|
75
|
+
* clientId will share rate limiters, while different clientIds get separate limiters.
|
|
76
|
+
*
|
|
77
|
+
* This implementation uses a sliding window to track all requests within the time window.
|
|
78
|
+
*/
|
|
79
|
+
function queueRequest(clientId, category, task, debug = false) {
|
|
80
|
+
const key = `${clientId}:${category}`;
|
|
81
|
+
if (!queues[key]) queues[key] = {
|
|
82
|
+
running: 0,
|
|
83
|
+
queue: [],
|
|
84
|
+
requestTimestamps: [],
|
|
85
|
+
nextTimer: null
|
|
86
|
+
};
|
|
87
|
+
const queue = queues[key];
|
|
88
|
+
const { max, perMs } = LIMITS[category] ?? LIMITS.default;
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const run = () => {
|
|
91
|
+
const now$1 = Date.now();
|
|
92
|
+
queue.requestTimestamps = cleanupTimestamps(queue.requestTimestamps, perMs, now$1);
|
|
93
|
+
if (queue.requestTimestamps.length >= max) {
|
|
94
|
+
const nextAvailable = getNextAvailableTime(queue.requestTimestamps, max, perMs, now$1);
|
|
95
|
+
const waitTime = nextAvailable - now$1;
|
|
96
|
+
if (debug) console.log(`[apiQueue] 🚫 Rate limit reached (${queue.requestTimestamps.length}/${max} in last ${perMs}ms). Queuing request. Need to wait ${waitTime.toFixed(0)}ms. Queue size: ${queue.queue.length + 1}`);
|
|
97
|
+
queue.queue.push({
|
|
98
|
+
run,
|
|
99
|
+
addedAt: now$1
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
queue.requestTimestamps.push(now$1);
|
|
104
|
+
queue.running++;
|
|
105
|
+
if (debug) console.log(`[apiQueue] ▶️ Executing request (${category}). Requests in window: ${queue.requestTimestamps.length}/${max}. Running: ${queue.running}. Queue size: ${queue.queue.length}`);
|
|
106
|
+
task().then(resolve).catch(reject).finally(() => {
|
|
107
|
+
queue.running--;
|
|
108
|
+
if (debug) console.log(`[apiQueue] ✅ Request completed (${category}). Requests in window: ${queue.requestTimestamps.length}/${max}. Running: ${queue.running}. Queue size: ${queue.queue.length}`);
|
|
109
|
+
processQueue(key, debug, category);
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
queue.requestTimestamps = cleanupTimestamps(queue.requestTimestamps, perMs, now);
|
|
114
|
+
if (queue.queue.length > 0 || queue.requestTimestamps.length >= max) {
|
|
115
|
+
queue.queue.push({
|
|
116
|
+
run,
|
|
117
|
+
addedAt: now
|
|
118
|
+
});
|
|
119
|
+
if (debug) console.log(`[apiQueue] ⏳ Queued request (${category}). Requests in window: ${queue.requestTimestamps.length}/${max}. Queue size: ${queue.queue.length}`);
|
|
120
|
+
processQueue(key, debug, category);
|
|
121
|
+
} else run();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function processQueue(key, debug, category) {
|
|
125
|
+
const queue = queues[key];
|
|
126
|
+
if (!queue || queue.queue.length === 0) return;
|
|
127
|
+
const { max, perMs } = LIMITS[category] ?? LIMITS.default;
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
queue.requestTimestamps = cleanupTimestamps(queue.requestTimestamps, perMs, now);
|
|
130
|
+
if (queue.requestTimestamps.length < max) {
|
|
131
|
+
const next = queue.queue.shift();
|
|
132
|
+
if (next) {
|
|
133
|
+
if (debug) {
|
|
134
|
+
const waitTime = Date.now() - next.addedAt;
|
|
135
|
+
console.log(`[apiQueue] 🚀 Processing queued request (${category}). Waited: ${waitTime.toFixed(0)}ms. Queue size: ${queue.queue.length}`);
|
|
136
|
+
}
|
|
137
|
+
next.run();
|
|
138
|
+
}
|
|
139
|
+
if (queue.queue.length > 0) processQueue(key, debug, category);
|
|
140
|
+
} else {
|
|
141
|
+
const nextAvailable = getNextAvailableTime(queue.requestTimestamps, max, perMs, now);
|
|
142
|
+
const delay = Math.max(0, nextAvailable - now + 50);
|
|
143
|
+
if (debug) console.log(`[apiQueue] ⏸️ Delaying queue processing (${category}). Will retry in ${delay.toFixed(0)}ms. Queue size: ${queue.queue.length}`);
|
|
144
|
+
if (!queue.nextTimer) queue.nextTimer = setTimeout(() => {
|
|
145
|
+
queue.nextTimer = null;
|
|
146
|
+
processQueue(key, debug, category);
|
|
147
|
+
}, delay);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Cleanup function to clear all queues and timers for a specific client.
|
|
152
|
+
* Useful for testing or cleanup on application shutdown.
|
|
153
|
+
*/
|
|
154
|
+
function clearQueue(clientId, category) {
|
|
155
|
+
if (category) {
|
|
156
|
+
const key = `${clientId}:${category}`;
|
|
157
|
+
const queue = queues[key];
|
|
158
|
+
if (queue) {
|
|
159
|
+
if (queue.nextTimer) {
|
|
160
|
+
clearTimeout(queue.nextTimer);
|
|
161
|
+
queue.nextTimer = null;
|
|
162
|
+
}
|
|
163
|
+
queue.queue = [];
|
|
164
|
+
queue.requestTimestamps = [];
|
|
165
|
+
queue.running = 0;
|
|
166
|
+
}
|
|
167
|
+
} else Object.keys(queues).forEach((key) => {
|
|
168
|
+
if (key.startsWith(`${clientId}:`)) {
|
|
169
|
+
const queue = queues[key];
|
|
170
|
+
if (queue?.nextTimer) {
|
|
171
|
+
clearTimeout(queue.nextTimer);
|
|
172
|
+
queue.nextTimer = null;
|
|
173
|
+
}
|
|
174
|
+
delete queues[key];
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Very naive path-based category detection. If no matcher fits, the `default` category
|
|
180
|
+
* (effectively unlimited) is returned. Adjust these heuristics as your API surface evolves.
|
|
181
|
+
*/
|
|
182
|
+
function categorizeRequest(path, method = "GET") {
|
|
183
|
+
const cleanPath = path.toLowerCase();
|
|
184
|
+
const isPost = method?.toUpperCase() === "POST";
|
|
185
|
+
if (cleanPath.includes("/documentsubmissions")) return isPost ? "submitDocuments" : "getSubmission";
|
|
186
|
+
if (cleanPath.includes("/documents/recent")) return "getRecentDocuments";
|
|
187
|
+
if (cleanPath.includes("/documents/search")) return "searchDocuments";
|
|
188
|
+
if (cleanPath.includes("/documents/state/") && cleanPath.endsWith("/state")) return "cancelDocument";
|
|
189
|
+
if (/\/documents\/[^/]+\/raw$/.test(cleanPath)) return "getDocument";
|
|
190
|
+
if (/\/documents\/[^/]+\/details$/.test(cleanPath)) return "getDocumentDetails";
|
|
191
|
+
if (cleanPath.includes("/taxpayer/search/tin")) return "searchTin";
|
|
192
|
+
if (cleanPath.includes("/taxpayer/validate/")) return "searchTin";
|
|
193
|
+
if (cleanPath.includes("/taxpayer/qrcode")) return "taxpayerQr";
|
|
194
|
+
if (cleanPath.includes("/searchtin")) return "searchTin";
|
|
195
|
+
if (cleanPath.includes("/qrcode")) return "taxpayerQr";
|
|
196
|
+
if (cleanPath.includes("/connect/token")) return "loginTaxpayer";
|
|
197
|
+
return "default";
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
//#endregion
|
|
201
|
+
export { categorizeRequest, clearQueue, queueRequest };
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/utils/apiQueue.ts
|
|
3
|
+
const queues = {};
|
|
4
|
+
const LIMITS = {
|
|
5
|
+
loginTaxpayer: {
|
|
6
|
+
max: 12,
|
|
7
|
+
perMs: 6e4
|
|
8
|
+
},
|
|
9
|
+
loginIntermediary: {
|
|
10
|
+
max: 12,
|
|
11
|
+
perMs: 6e4
|
|
12
|
+
},
|
|
13
|
+
submitDocuments: {
|
|
14
|
+
max: 100,
|
|
15
|
+
perMs: 6e4
|
|
16
|
+
},
|
|
17
|
+
getSubmission: {
|
|
18
|
+
max: 300,
|
|
19
|
+
perMs: 6e4
|
|
20
|
+
},
|
|
21
|
+
cancelDocument: {
|
|
22
|
+
max: 12,
|
|
23
|
+
perMs: 6e4
|
|
24
|
+
},
|
|
25
|
+
rejectDocument: {
|
|
26
|
+
max: 12,
|
|
27
|
+
perMs: 6e4
|
|
28
|
+
},
|
|
29
|
+
getDocument: {
|
|
30
|
+
max: 60,
|
|
31
|
+
perMs: 6e4
|
|
32
|
+
},
|
|
33
|
+
getDocumentDetails: {
|
|
34
|
+
max: 125,
|
|
35
|
+
perMs: 6e4
|
|
36
|
+
},
|
|
37
|
+
getRecentDocuments: {
|
|
38
|
+
max: 12,
|
|
39
|
+
perMs: 6e4
|
|
40
|
+
},
|
|
41
|
+
searchDocuments: {
|
|
42
|
+
max: 12,
|
|
43
|
+
perMs: 6e4
|
|
44
|
+
},
|
|
45
|
+
searchTin: {
|
|
46
|
+
max: 60,
|
|
47
|
+
perMs: 6e4
|
|
48
|
+
},
|
|
49
|
+
taxpayerQr: {
|
|
50
|
+
max: 60,
|
|
51
|
+
perMs: 6e4
|
|
52
|
+
},
|
|
53
|
+
default: {
|
|
54
|
+
max: 12,
|
|
55
|
+
perMs: 6e4
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Clean up old timestamps outside the sliding window
|
|
60
|
+
*/
|
|
61
|
+
function cleanupTimestamps(timestamps, windowMs, now) {
|
|
62
|
+
return timestamps.filter((ts) => now - ts < windowMs);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Calculate when we can make the next request without exceeding the rate limit
|
|
66
|
+
*/
|
|
67
|
+
function getNextAvailableTime(timestamps, max, windowMs, now) {
|
|
68
|
+
if (timestamps.length < max) return now;
|
|
69
|
+
const oldestTimestamp = timestamps[0];
|
|
70
|
+
if (!oldestTimestamp) return now;
|
|
71
|
+
return oldestTimestamp + windowMs;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Public helper to schedule a request according to the category's limits.
|
|
75
|
+
* Rate limits are enforced per clientId, so multiple instances with the same
|
|
76
|
+
* clientId will share rate limiters, while different clientIds get separate limiters.
|
|
77
|
+
*
|
|
78
|
+
* This implementation uses a sliding window to track all requests within the time window.
|
|
79
|
+
*/
|
|
80
|
+
function queueRequest(clientId, category, task, debug = false) {
|
|
81
|
+
const key = `${clientId}:${category}`;
|
|
82
|
+
if (!queues[key]) queues[key] = {
|
|
83
|
+
running: 0,
|
|
84
|
+
queue: [],
|
|
85
|
+
requestTimestamps: [],
|
|
86
|
+
nextTimer: null
|
|
87
|
+
};
|
|
88
|
+
const queue = queues[key];
|
|
89
|
+
const { max, perMs } = LIMITS[category] ?? LIMITS.default;
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const run = () => {
|
|
92
|
+
const now$1 = Date.now();
|
|
93
|
+
queue.requestTimestamps = cleanupTimestamps(queue.requestTimestamps, perMs, now$1);
|
|
94
|
+
if (queue.requestTimestamps.length >= max) {
|
|
95
|
+
const nextAvailable = getNextAvailableTime(queue.requestTimestamps, max, perMs, now$1);
|
|
96
|
+
const waitTime = nextAvailable - now$1;
|
|
97
|
+
if (debug) console.log(`[apiQueue] 🚫 Rate limit reached (${queue.requestTimestamps.length}/${max} in last ${perMs}ms). Queuing request. Need to wait ${waitTime.toFixed(0)}ms. Queue size: ${queue.queue.length + 1}`);
|
|
98
|
+
queue.queue.push({
|
|
99
|
+
run,
|
|
100
|
+
addedAt: now$1
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
queue.requestTimestamps.push(now$1);
|
|
105
|
+
queue.running++;
|
|
106
|
+
if (debug) console.log(`[apiQueue] ▶️ Executing request (${category}). Requests in window: ${queue.requestTimestamps.length}/${max}. Running: ${queue.running}. Queue size: ${queue.queue.length}`);
|
|
107
|
+
task().then(resolve).catch(reject).finally(() => {
|
|
108
|
+
queue.running--;
|
|
109
|
+
if (debug) console.log(`[apiQueue] ✅ Request completed (${category}). Requests in window: ${queue.requestTimestamps.length}/${max}. Running: ${queue.running}. Queue size: ${queue.queue.length}`);
|
|
110
|
+
processQueue(key, debug, category);
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
queue.requestTimestamps = cleanupTimestamps(queue.requestTimestamps, perMs, now);
|
|
115
|
+
if (queue.queue.length > 0 || queue.requestTimestamps.length >= max) {
|
|
116
|
+
queue.queue.push({
|
|
117
|
+
run,
|
|
118
|
+
addedAt: now
|
|
119
|
+
});
|
|
120
|
+
if (debug) console.log(`[apiQueue] ⏳ Queued request (${category}). Requests in window: ${queue.requestTimestamps.length}/${max}. Queue size: ${queue.queue.length}`);
|
|
121
|
+
processQueue(key, debug, category);
|
|
122
|
+
} else run();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function processQueue(key, debug, category) {
|
|
126
|
+
const queue = queues[key];
|
|
127
|
+
if (!queue || queue.queue.length === 0) return;
|
|
128
|
+
const { max, perMs } = LIMITS[category] ?? LIMITS.default;
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
queue.requestTimestamps = cleanupTimestamps(queue.requestTimestamps, perMs, now);
|
|
131
|
+
if (queue.requestTimestamps.length < max) {
|
|
132
|
+
const next = queue.queue.shift();
|
|
133
|
+
if (next) {
|
|
134
|
+
if (debug) {
|
|
135
|
+
const waitTime = Date.now() - next.addedAt;
|
|
136
|
+
console.log(`[apiQueue] 🚀 Processing queued request (${category}). Waited: ${waitTime.toFixed(0)}ms. Queue size: ${queue.queue.length}`);
|
|
137
|
+
}
|
|
138
|
+
next.run();
|
|
139
|
+
}
|
|
140
|
+
if (queue.queue.length > 0) processQueue(key, debug, category);
|
|
141
|
+
} else {
|
|
142
|
+
const nextAvailable = getNextAvailableTime(queue.requestTimestamps, max, perMs, now);
|
|
143
|
+
const delay = Math.max(0, nextAvailable - now + 50);
|
|
144
|
+
if (debug) console.log(`[apiQueue] ⏸️ Delaying queue processing (${category}). Will retry in ${delay.toFixed(0)}ms. Queue size: ${queue.queue.length}`);
|
|
145
|
+
if (!queue.nextTimer) queue.nextTimer = setTimeout(() => {
|
|
146
|
+
queue.nextTimer = null;
|
|
147
|
+
processQueue(key, debug, category);
|
|
148
|
+
}, delay);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Cleanup function to clear all queues and timers for a specific client.
|
|
153
|
+
* Useful for testing or cleanup on application shutdown.
|
|
154
|
+
*/
|
|
155
|
+
function clearQueue(clientId, category) {
|
|
156
|
+
if (category) {
|
|
157
|
+
const key = `${clientId}:${category}`;
|
|
158
|
+
const queue = queues[key];
|
|
159
|
+
if (queue) {
|
|
160
|
+
if (queue.nextTimer) {
|
|
161
|
+
clearTimeout(queue.nextTimer);
|
|
162
|
+
queue.nextTimer = null;
|
|
163
|
+
}
|
|
164
|
+
queue.queue = [];
|
|
165
|
+
queue.requestTimestamps = [];
|
|
166
|
+
queue.running = 0;
|
|
167
|
+
}
|
|
168
|
+
} else Object.keys(queues).forEach((key) => {
|
|
169
|
+
if (key.startsWith(`${clientId}:`)) {
|
|
170
|
+
const queue = queues[key];
|
|
171
|
+
if (queue?.nextTimer) {
|
|
172
|
+
clearTimeout(queue.nextTimer);
|
|
173
|
+
queue.nextTimer = null;
|
|
174
|
+
}
|
|
175
|
+
delete queues[key];
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Very naive path-based category detection. If no matcher fits, the `default` category
|
|
181
|
+
* (effectively unlimited) is returned. Adjust these heuristics as your API surface evolves.
|
|
182
|
+
*/
|
|
183
|
+
function categorizeRequest(path, method = "GET") {
|
|
184
|
+
const cleanPath = path.toLowerCase();
|
|
185
|
+
const isPost = method?.toUpperCase() === "POST";
|
|
186
|
+
if (cleanPath.includes("/documentsubmissions")) return isPost ? "submitDocuments" : "getSubmission";
|
|
187
|
+
if (cleanPath.includes("/documents/recent")) return "getRecentDocuments";
|
|
188
|
+
if (cleanPath.includes("/documents/search")) return "searchDocuments";
|
|
189
|
+
if (cleanPath.includes("/documents/state/") && cleanPath.endsWith("/state")) return "cancelDocument";
|
|
190
|
+
if (/\/documents\/[^/]+\/raw$/.test(cleanPath)) return "getDocument";
|
|
191
|
+
if (/\/documents\/[^/]+\/details$/.test(cleanPath)) return "getDocumentDetails";
|
|
192
|
+
if (cleanPath.includes("/taxpayer/search/tin")) return "searchTin";
|
|
193
|
+
if (cleanPath.includes("/taxpayer/validate/")) return "searchTin";
|
|
194
|
+
if (cleanPath.includes("/taxpayer/qrcode")) return "taxpayerQr";
|
|
195
|
+
if (cleanPath.includes("/searchtin")) return "searchTin";
|
|
196
|
+
if (cleanPath.includes("/qrcode")) return "taxpayerQr";
|
|
197
|
+
if (cleanPath.includes("/connect/token")) return "loginTaxpayer";
|
|
198
|
+
return "default";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//#endregion
|
|
202
|
+
Object.defineProperty(exports, 'categorizeRequest', {
|
|
203
|
+
enumerable: true,
|
|
204
|
+
get: function () {
|
|
205
|
+
return categorizeRequest;
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
Object.defineProperty(exports, 'clearQueue', {
|
|
209
|
+
enumerable: true,
|
|
210
|
+
get: function () {
|
|
211
|
+
return clearQueue;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
Object.defineProperty(exports, 'queueRequest', {
|
|
215
|
+
enumerable: true,
|
|
216
|
+
get: function () {
|
|
217
|
+
return queueRequest;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
//# sourceMappingURL=apiQueue-DgKWaQDS.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiQueue-DgKWaQDS.cjs","names":["queues: Record<string, Queue>","LIMITS: Record<ApiCategory, { max: number; perMs: number }>","timestamps: number[]","windowMs: number","now: number","max: number","clientId: string","category: ApiCategory","task: Task<T>","now","key: string","debug: boolean","category?: ApiCategory","path: string","method: string"],"sources":["../src/utils/apiQueue.ts"],"sourcesContent":["// A very small utility that provides per-endpoint request queuing with rate-limits.\n// The goal is to make sure that we never exceed the vendor-defined limits while also ensuring\n// that every request is eventually executed.\n//\n// NOTE: This is intentionally minimal – no external dependencies are introduced.\n// If you need more advanced features (persistence, jitter, etc.) consider a library such as `bottleneck`.\n\n/*\nRate-limit specification (per 60-second window)\n----------------------------------------------\nLogin as Taxpayer System : 12\nLogin as Intermediary System : 12\nSubmit Documents : 100\nGet Submission : 300\nCancel Document : 12\nReject Document : 12\nGet Document : 60\nGet Document Details : 125\nGet Recent Documents : 12\nSearch Documents : 12\nSearch Taxpayer's TIN : 60\nTaxpayer's QR Code : 60\n*/\n\nexport type ApiCategory =\n | 'loginTaxpayer'\n | 'loginIntermediary'\n | 'submitDocuments'\n | 'getSubmission'\n | 'cancelDocument'\n | 'rejectDocument'\n | 'getDocument'\n | 'getDocumentDetails'\n | 'getRecentDocuments'\n | 'searchDocuments'\n | 'searchTin'\n | 'taxpayerQr'\n | 'default'\n\ntype Task<T> = () => Promise<T>\n\ninterface Queue {\n running: number\n queue: Array<{ run: () => void; addedAt: number }>\n requestTimestamps: number[] // Track all request timestamps in the sliding window\n nextTimer: NodeJS.Timeout | null // Track scheduled timer to avoid pile-up\n}\n\nconst queues: Record<string, Queue> = {}\n\n// Rate limits: max requests per time window (in ms)\nconst LIMITS: Record<ApiCategory, { max: number; perMs: number }> = {\n loginTaxpayer: { max: 12, perMs: 60000 }, // 12 req/60s\n loginIntermediary: { max: 12, perMs: 60000 }, // 12 req/60s\n submitDocuments: { max: 100, perMs: 60000 }, // 100 req/60s\n getSubmission: { max: 300, perMs: 60000 }, // 300 req/60s\n cancelDocument: { max: 12, perMs: 60000 }, // 12 req/60s\n rejectDocument: { max: 12, perMs: 60000 }, // 12 req/60s\n getDocument: { max: 60, perMs: 60000 }, // 60 req/60s\n getDocumentDetails: { max: 125, perMs: 60000 }, // 125 req/60s\n getRecentDocuments: { max: 12, perMs: 60000 }, // 12 req/60s\n searchDocuments: { max: 12, perMs: 60000 }, // 12 req/60s\n searchTin: { max: 60, perMs: 60000 }, // 60 req/60s\n taxpayerQr: { max: 60, perMs: 60000 }, // 60 req/60s\n default: { max: 12, perMs: 60000 }, // 12 req/60s (minimum limit)\n}\n\n/**\n * Clean up old timestamps outside the sliding window\n */\nfunction cleanupTimestamps(\n timestamps: number[],\n windowMs: number,\n now: number,\n): number[] {\n return timestamps.filter(ts => now - ts < windowMs)\n}\n\n/**\n * Calculate when we can make the next request without exceeding the rate limit\n */\nfunction getNextAvailableTime(\n timestamps: number[],\n max: number,\n windowMs: number,\n now: number,\n): number {\n if (timestamps.length < max) {\n return now // Can execute immediately\n }\n\n // We're at the limit. Find when the oldest request will expire\n const oldestTimestamp = timestamps[0]\n if (!oldestTimestamp) {\n return now // Shouldn't happen, but fallback to now\n }\n return oldestTimestamp + windowMs\n}\n\n/**\n * Public helper to schedule a request according to the category's limits.\n * Rate limits are enforced per clientId, so multiple instances with the same\n * clientId will share rate limiters, while different clientIds get separate limiters.\n *\n * This implementation uses a sliding window to track all requests within the time window.\n */\nexport function queueRequest<T>(\n clientId: string,\n category: ApiCategory,\n task: Task<T>,\n debug = false,\n): Promise<T> {\n const key = `${clientId}:${category}`\n if (!queues[key]) {\n queues[key] = {\n running: 0,\n queue: [],\n requestTimestamps: [],\n nextTimer: null,\n }\n }\n\n const queue = queues[key]!\n const { max, perMs } = LIMITS[category] ?? LIMITS.default\n\n return new Promise<T>((resolve, reject) => {\n const run = () => {\n const now = Date.now()\n\n // Clean up old timestamps before checking\n queue.requestTimestamps = cleanupTimestamps(\n queue.requestTimestamps,\n perMs,\n now,\n )\n\n // Check if we can execute now\n if (queue.requestTimestamps.length >= max) {\n // We've hit the rate limit, need to wait\n const nextAvailable = getNextAvailableTime(\n queue.requestTimestamps,\n max,\n perMs,\n now,\n )\n const waitTime = nextAvailable - now\n\n if (debug) {\n console.log(\n `[apiQueue] 🚫 Rate limit reached (${queue.requestTimestamps.length}/${max} in last ${perMs}ms). Queuing request. Need to wait ${waitTime.toFixed(0)}ms. Queue size: ${queue.queue.length + 1}`,\n )\n }\n\n // Re-queue this request to try again later\n queue.queue.push({ run, addedAt: now })\n // Don't call processQueue here - let the scheduled timer handle it\n // to avoid potential requeue loops\n return\n }\n\n // Record this request timestamp\n queue.requestTimestamps.push(now)\n queue.running++\n\n if (debug) {\n console.log(\n `[apiQueue] ▶️ Executing request (${category}). Requests in window: ${queue.requestTimestamps.length}/${max}. Running: ${queue.running}. Queue size: ${queue.queue.length}`,\n )\n }\n\n task()\n .then(resolve)\n .catch(reject)\n .finally(() => {\n queue.running--\n if (debug) {\n console.log(\n `[apiQueue] ✅ Request completed (${category}). Requests in window: ${queue.requestTimestamps.length}/${max}. Running: ${queue.running}. Queue size: ${queue.queue.length}`,\n )\n }\n processQueue(key, debug, category)\n })\n }\n\n // Add to queue or run immediately\n const now = Date.now()\n queue.requestTimestamps = cleanupTimestamps(\n queue.requestTimestamps,\n perMs,\n now,\n )\n\n if (queue.queue.length > 0 || queue.requestTimestamps.length >= max) {\n // Either there's already a queue, or we're at the rate limit\n queue.queue.push({ run, addedAt: now })\n if (debug) {\n console.log(\n `[apiQueue] ⏳ Queued request (${category}). Requests in window: ${queue.requestTimestamps.length}/${max}. Queue size: ${queue.queue.length}`,\n )\n }\n processQueue(key, debug, category)\n } else {\n // Can run immediately\n run()\n }\n })\n}\n\nfunction processQueue(key: string, debug: boolean, category: ApiCategory) {\n const queue = queues[key]\n if (!queue || queue.queue.length === 0) return\n\n const { max, perMs } = LIMITS[category] ?? LIMITS.default\n const now = Date.now()\n\n // Clean up old timestamps\n queue.requestTimestamps = cleanupTimestamps(\n queue.requestTimestamps,\n perMs,\n now,\n )\n\n // Check if we can process the next request\n if (queue.requestTimestamps.length < max) {\n // We have capacity, process immediately\n const next = queue.queue.shift()\n if (next) {\n if (debug) {\n const waitTime = Date.now() - next.addedAt\n console.log(\n `[apiQueue] 🚀 Processing queued request (${category}). Waited: ${waitTime.toFixed(0)}ms. Queue size: ${queue.queue.length}`,\n )\n }\n next.run()\n }\n // After one runs, immediately try again (in case there's more space)\n if (queue.queue.length > 0) {\n processQueue(key, debug, category)\n }\n } else {\n // We're at capacity, schedule for when the oldest request expires\n const nextAvailable = getNextAvailableTime(\n queue.requestTimestamps,\n max,\n perMs,\n now,\n )\n const delay = Math.max(0, nextAvailable - now + 50) // +50ms buffer for timer precision\n\n if (debug) {\n console.log(\n `[apiQueue] ⏸️ Delaying queue processing (${category}). Will retry in ${delay.toFixed(0)}ms. Queue size: ${queue.queue.length}`,\n )\n }\n\n // Only schedule a timer if one isn't already scheduled\n // This prevents timer pile-up during bursts\n if (!queue.nextTimer) {\n queue.nextTimer = setTimeout(() => {\n queue.nextTimer = null\n processQueue(key, debug, category)\n }, delay)\n }\n }\n}\n\n/**\n * Cleanup function to clear all queues and timers for a specific client.\n * Useful for testing or cleanup on application shutdown.\n */\nexport function clearQueue(clientId: string, category?: ApiCategory): void {\n if (category) {\n const key = `${clientId}:${category}`\n const queue = queues[key]\n if (queue) {\n if (queue.nextTimer) {\n clearTimeout(queue.nextTimer)\n queue.nextTimer = null\n }\n queue.queue = []\n queue.requestTimestamps = []\n queue.running = 0\n }\n } else {\n // Clear all queues for this client\n Object.keys(queues).forEach(key => {\n if (key.startsWith(`${clientId}:`)) {\n const queue = queues[key]\n if (queue?.nextTimer) {\n clearTimeout(queue.nextTimer)\n queue.nextTimer = null\n }\n delete queues[key]\n }\n })\n }\n}\n\n/**\n * Very naive path-based category detection. If no matcher fits, the `default` category\n * (effectively unlimited) is returned. Adjust these heuristics as your API surface evolves.\n */\nexport function categorizeRequest(\n path: string,\n method: string = 'GET',\n): ApiCategory {\n const cleanPath = path.toLowerCase()\n const isPost = method?.toUpperCase() === 'POST'\n\n if (cleanPath.includes('/documentsubmissions')) {\n return isPost ? 'submitDocuments' : 'getSubmission'\n }\n\n // -----------------------------\n // v1.0 API endpoint matchers\n // -----------------------------\n\n // Get Recent Documents - /api/v1.0/documents/recent\n if (cleanPath.includes('/documents/recent')) {\n return 'getRecentDocuments'\n }\n\n // Search Documents - /api/v1.0/documents/search\n if (cleanPath.includes('/documents/search')) {\n return 'searchDocuments'\n }\n\n // Document state actions (cancel/reject) - PUT /api/v1.0/documents/state/{uuid}/state\n // Both cancel and reject use the same endpoint, differentiated only by request body\n if (cleanPath.includes('/documents/state/') && cleanPath.endsWith('/state')) {\n // Both cancelDocument and rejectDocument share the same rate limit (12 RPM)\n // Use cancelDocument category for both since they share the same bucket\n return 'cancelDocument'\n }\n\n // Document raw content - /api/v1.0/documents/{uuid}/raw\n if (/\\/documents\\/[^/]+\\/raw$/.test(cleanPath)) {\n return 'getDocument'\n }\n\n // Document details - /api/v1.0/documents/{uuid}/details\n if (/\\/documents\\/[^/]+\\/details$/.test(cleanPath)) {\n return 'getDocumentDetails'\n }\n\n // Taxpayer TIN search & validation share same limit bucket\n if (cleanPath.includes('/taxpayer/search/tin')) return 'searchTin'\n if (cleanPath.includes('/taxpayer/validate/')) return 'searchTin'\n\n // Taxpayer QR code info\n if (cleanPath.includes('/taxpayer/qrcode')) return 'taxpayerQr'\n\n // Legacy matchers (kept for backward compatibility)\n if (cleanPath.includes('/searchtin')) return 'searchTin'\n if (cleanPath.includes('/qrcode')) return 'taxpayerQr'\n if (cleanPath.includes('/connect/token')) {\n return 'loginTaxpayer'\n }\n\n return 'default'\n}\n"],"mappings":"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"}
|
|
@@ -173,6 +173,10 @@ const generateCleanInvoiceObject = (invoice) => {
|
|
|
173
173
|
_: invoice.legalMonetaryTotal.taxInclusiveAmount,
|
|
174
174
|
currencyID: invoice.invoiceCurrencyCode
|
|
175
175
|
}],
|
|
176
|
+
PayableRoundingAmount: [{
|
|
177
|
+
_: invoice.legalMonetaryTotal.payableRoundingAmount,
|
|
178
|
+
currencyID: invoice.invoiceCurrencyCode
|
|
179
|
+
}],
|
|
176
180
|
PayableAmount: [{
|
|
177
181
|
_: invoice.legalMonetaryTotal.payableAmount,
|
|
178
182
|
currencyID: invoice.invoiceCurrencyCode
|
|
@@ -545,7 +549,7 @@ const generateCompleteDocument = (invoices, signingCredentials) => {
|
|
|
545
549
|
* Creates a line item with percentage-based taxation (e.g., SST, GST)
|
|
546
550
|
*/
|
|
547
551
|
const createPercentageTaxLineItem = (params) => {
|
|
548
|
-
const quantity = params.quantity
|
|
552
|
+
const quantity = params.quantity ? params.quantity <= 0 ? 1 : params.quantity ?? 1 : 1;
|
|
549
553
|
const preDiscountAmount = params.unitPrice * quantity;
|
|
550
554
|
const hasDiscount = params.discountAmount !== void 0 || params.discountRate !== void 0;
|
|
551
555
|
const computedDiscountByRate = hasDiscount && params.discountRate !== void 0 ? preDiscountAmount * params.discountRate : 0;
|
|
@@ -594,15 +598,17 @@ const createFixedRateTaxLineItem = (params) => {
|
|
|
594
598
|
/**
|
|
595
599
|
* Calculates invoice totals from line items
|
|
596
600
|
*/
|
|
597
|
-
const calculateInvoiceTotals = (lineItems) => {
|
|
601
|
+
const calculateInvoiceTotals = (lineItems, payableRoundingAmount = 0) => {
|
|
598
602
|
const taxExclusiveAmount = lineItems.reduce((sum, item) => sum + item.totalTaxableAmountPerLine, 0);
|
|
599
603
|
const totalTaxAmount = lineItems.reduce((sum, item) => sum + item.taxAmount, 0);
|
|
600
604
|
const taxInclusiveAmount = taxExclusiveAmount + totalTaxAmount;
|
|
605
|
+
const payableAmount = taxInclusiveAmount + payableRoundingAmount;
|
|
601
606
|
return {
|
|
602
607
|
legalMonetaryTotal: {
|
|
603
608
|
taxExclusiveAmount: Math.round(taxExclusiveAmount * 100) / 100,
|
|
604
609
|
taxInclusiveAmount: Math.round(taxInclusiveAmount * 100) / 100,
|
|
605
|
-
|
|
610
|
+
payableRoundingAmount: Math.round(payableRoundingAmount * 100) / 100,
|
|
611
|
+
payableAmount: Math.round(payableAmount * 100) / 100
|
|
606
612
|
},
|
|
607
613
|
taxTotal: { taxAmount: Math.round(totalTaxAmount * 100) / 100 }
|
|
608
614
|
};
|
|
@@ -174,6 +174,10 @@ const generateCleanInvoiceObject = (invoice) => {
|
|
|
174
174
|
_: invoice.legalMonetaryTotal.taxInclusiveAmount,
|
|
175
175
|
currencyID: invoice.invoiceCurrencyCode
|
|
176
176
|
}],
|
|
177
|
+
PayableRoundingAmount: [{
|
|
178
|
+
_: invoice.legalMonetaryTotal.payableRoundingAmount,
|
|
179
|
+
currencyID: invoice.invoiceCurrencyCode
|
|
180
|
+
}],
|
|
177
181
|
PayableAmount: [{
|
|
178
182
|
_: invoice.legalMonetaryTotal.payableAmount,
|
|
179
183
|
currencyID: invoice.invoiceCurrencyCode
|
|
@@ -546,7 +550,7 @@ const generateCompleteDocument = (invoices, signingCredentials) => {
|
|
|
546
550
|
* Creates a line item with percentage-based taxation (e.g., SST, GST)
|
|
547
551
|
*/
|
|
548
552
|
const createPercentageTaxLineItem = (params) => {
|
|
549
|
-
const quantity = params.quantity
|
|
553
|
+
const quantity = params.quantity ? params.quantity <= 0 ? 1 : params.quantity ?? 1 : 1;
|
|
550
554
|
const preDiscountAmount = params.unitPrice * quantity;
|
|
551
555
|
const hasDiscount = params.discountAmount !== void 0 || params.discountRate !== void 0;
|
|
552
556
|
const computedDiscountByRate = hasDiscount && params.discountRate !== void 0 ? preDiscountAmount * params.discountRate : 0;
|
|
@@ -595,15 +599,17 @@ const createFixedRateTaxLineItem = (params) => {
|
|
|
595
599
|
/**
|
|
596
600
|
* Calculates invoice totals from line items
|
|
597
601
|
*/
|
|
598
|
-
const calculateInvoiceTotals = (lineItems) => {
|
|
602
|
+
const calculateInvoiceTotals = (lineItems, payableRoundingAmount = 0) => {
|
|
599
603
|
const taxExclusiveAmount = lineItems.reduce((sum, item) => sum + item.totalTaxableAmountPerLine, 0);
|
|
600
604
|
const totalTaxAmount = lineItems.reduce((sum, item) => sum + item.taxAmount, 0);
|
|
601
605
|
const taxInclusiveAmount = taxExclusiveAmount + totalTaxAmount;
|
|
606
|
+
const payableAmount = taxInclusiveAmount + payableRoundingAmount;
|
|
602
607
|
return {
|
|
603
608
|
legalMonetaryTotal: {
|
|
604
609
|
taxExclusiveAmount: Math.round(taxExclusiveAmount * 100) / 100,
|
|
605
610
|
taxInclusiveAmount: Math.round(taxInclusiveAmount * 100) / 100,
|
|
606
|
-
|
|
611
|
+
payableRoundingAmount: Math.round(payableRoundingAmount * 100) / 100,
|
|
612
|
+
payableAmount: Math.round(payableAmount * 100) / 100
|
|
607
613
|
},
|
|
608
614
|
taxTotal: { taxAmount: Math.round(totalTaxAmount * 100) / 100 }
|
|
609
615
|
};
|
|
@@ -724,4 +730,4 @@ Object.defineProperty(exports, 'transformDocumentForHashing', {
|
|
|
724
730
|
return transformDocumentForHashing;
|
|
725
731
|
}
|
|
726
732
|
});
|
|
727
|
-
//# sourceMappingURL=document-
|
|
733
|
+
//# sourceMappingURL=document-DoQEvmcK.cjs.map
|