@danielarndt0/cnpj-db-loader 2.4.0-beta.3 → 2.4.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/cli.js +481 -219
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +317 -296
- package/dist/index.js +360 -214
- package/dist/index.js.map +1 -1
- package/docs/commands.md +6 -0
- package/docs/federal-revenue.md +36 -2
- package/package.json +1 -1
- package/docs/releases/v2.4.0-beta.3.md +0 -42
package/dist/index.js
CHANGED
|
@@ -87,6 +87,222 @@ function getConfigFilePath() {
|
|
|
87
87
|
return path2.join(os2.homedir(), ".config", "cnpj-db-loader", "config.json");
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// src/services/federal-revenue/client.ts
|
|
91
|
+
var DEFAULT_FEDERAL_REVENUE_WEBDAV_URL = "https://arquivos.receitafederal.gov.br/public.php/webdav";
|
|
92
|
+
var DEFAULT_FEDERAL_REVENUE_USER_AGENT = "cnpj-db-loader federal-revenue-client";
|
|
93
|
+
var REFERENCE_PATTERN = /^\d{4}-\d{2}$/;
|
|
94
|
+
function trimTrailingSlash(value) {
|
|
95
|
+
return value.replace(/\/+$/g, "");
|
|
96
|
+
}
|
|
97
|
+
function normalizeBaseUrl(value) {
|
|
98
|
+
return trimTrailingSlash(value ?? DEFAULT_FEDERAL_REVENUE_WEBDAV_URL);
|
|
99
|
+
}
|
|
100
|
+
function getShareToken(value) {
|
|
101
|
+
const shareToken = value?.trim();
|
|
102
|
+
if (!shareToken) {
|
|
103
|
+
throw new ValidationError(
|
|
104
|
+
"Federal Revenue public share token is not configured. Run `cnpj-db-loader federal-revenue config set share-token <token>` or pass --share-token."
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return shareToken;
|
|
108
|
+
}
|
|
109
|
+
function encodePathSegment(value) {
|
|
110
|
+
return encodeURIComponent(value).replace(/%2F/gi, "/");
|
|
111
|
+
}
|
|
112
|
+
function decodeXml(value) {
|
|
113
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
114
|
+
}
|
|
115
|
+
function decodeHrefSegment(value) {
|
|
116
|
+
try {
|
|
117
|
+
return decodeURIComponent(value);
|
|
118
|
+
} catch {
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function getAuthHeader(shareToken) {
|
|
123
|
+
return `Basic ${Buffer.from(`${shareToken}:`).toString("base64")}`;
|
|
124
|
+
}
|
|
125
|
+
function buildUrl(baseUrl, segments = []) {
|
|
126
|
+
if (segments.length === 0) {
|
|
127
|
+
return `${baseUrl}/`;
|
|
128
|
+
}
|
|
129
|
+
return `${baseUrl}/${segments.map(encodePathSegment).join("/")}`;
|
|
130
|
+
}
|
|
131
|
+
function extractFirst(block, tagName) {
|
|
132
|
+
const pattern = new RegExp(
|
|
133
|
+
`<(?:[a-zA-Z0-9_-]+:)?${tagName}\\b[^>]*>([\\s\\S]*?)<\\/(?:[a-zA-Z0-9_-]+:)?${tagName}>`,
|
|
134
|
+
"i"
|
|
135
|
+
);
|
|
136
|
+
const match = block.match(pattern);
|
|
137
|
+
return match?.[1] ? decodeXml(match[1].trim()) : void 0;
|
|
138
|
+
}
|
|
139
|
+
function isCollectionResponse(block) {
|
|
140
|
+
return /<(?:[a-zA-Z0-9_-]+:)?collection\b/i.test(block);
|
|
141
|
+
}
|
|
142
|
+
function getNameFromHref(href) {
|
|
143
|
+
const cleanHref = href.split("?")[0] ?? href;
|
|
144
|
+
const withoutTrailingSlash = cleanHref.replace(/\/+$/g, "");
|
|
145
|
+
const rawName = withoutTrailingSlash.split("/").pop() ?? withoutTrailingSlash;
|
|
146
|
+
return decodeHrefSegment(rawName);
|
|
147
|
+
}
|
|
148
|
+
function parsePropfindXml(xml) {
|
|
149
|
+
const responseBlocks = xml.match(
|
|
150
|
+
/<(?:[a-zA-Z0-9_-]+:)?response\b[\s\S]*?<\/(?:[a-zA-Z0-9_-]+:)?response>/gi
|
|
151
|
+
);
|
|
152
|
+
if (!responseBlocks) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
return responseBlocks.map((block) => {
|
|
156
|
+
const href = extractFirst(block, "href");
|
|
157
|
+
if (!href) {
|
|
158
|
+
return void 0;
|
|
159
|
+
}
|
|
160
|
+
const size = extractFirst(block, "getcontentlength");
|
|
161
|
+
const parsedSize = size ? Number.parseInt(size, 10) : void 0;
|
|
162
|
+
const lastModified = extractFirst(block, "getlastmodified");
|
|
163
|
+
const etag = extractFirst(block, "getetag");
|
|
164
|
+
return {
|
|
165
|
+
href,
|
|
166
|
+
name: getNameFromHref(href),
|
|
167
|
+
isCollection: isCollectionResponse(block),
|
|
168
|
+
...Number.isFinite(parsedSize) ? { sizeInBytes: parsedSize } : {},
|
|
169
|
+
...lastModified ? { lastModified } : {},
|
|
170
|
+
...etag ? { etag } : {}
|
|
171
|
+
};
|
|
172
|
+
}).filter((entry) => entry !== void 0);
|
|
173
|
+
}
|
|
174
|
+
async function propfind(pathSegments, options = {}) {
|
|
175
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
176
|
+
const shareToken = getShareToken(options.shareToken);
|
|
177
|
+
let response;
|
|
178
|
+
try {
|
|
179
|
+
response = await fetch(buildUrl(baseUrl, pathSegments), {
|
|
180
|
+
method: "PROPFIND",
|
|
181
|
+
headers: {
|
|
182
|
+
Accept: "application/xml,text/xml,*/*",
|
|
183
|
+
Authorization: getAuthHeader(shareToken),
|
|
184
|
+
Depth: "1",
|
|
185
|
+
"User-Agent": options.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
} catch (error) {
|
|
189
|
+
throw new ValidationError(
|
|
190
|
+
`Federal Revenue WebDAV request failed before receiving a response: ${error instanceof Error ? error.message : String(error)}.`,
|
|
191
|
+
{ baseUrl, pathSegments }
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
throw new ValidationError(
|
|
196
|
+
`Federal Revenue WebDAV request failed with status ${response.status} ${response.statusText}.`,
|
|
197
|
+
{ status: response.status, statusText: response.statusText }
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
const xml = await response.text();
|
|
201
|
+
return {
|
|
202
|
+
entries: parsePropfindXml(xml),
|
|
203
|
+
baseUrl,
|
|
204
|
+
shareToken
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function validateFederalRevenueReference(reference) {
|
|
208
|
+
if (!REFERENCE_PATTERN.test(reference)) {
|
|
209
|
+
throw new ValidationError(
|
|
210
|
+
`Federal Revenue reference is invalid: ${reference}. Expected YYYY-MM.`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function getCurrentFederalRevenueReference(date = /* @__PURE__ */ new Date()) {
|
|
215
|
+
const year = date.getFullYear();
|
|
216
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
217
|
+
return `${year}-${month}`;
|
|
218
|
+
}
|
|
219
|
+
async function listFederalRevenueReferences(options = {}) {
|
|
220
|
+
const result = await propfind([], options);
|
|
221
|
+
const references = result.entries.filter((entry) => entry.isCollection && REFERENCE_PATTERN.test(entry.name)).map((entry) => ({
|
|
222
|
+
reference: entry.name,
|
|
223
|
+
href: entry.href
|
|
224
|
+
})).sort((left, right) => left.reference.localeCompare(right.reference));
|
|
225
|
+
return {
|
|
226
|
+
references,
|
|
227
|
+
remoteBaseUrl: result.baseUrl
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
async function resolveFederalRevenueReference(input = {}) {
|
|
231
|
+
const { references } = await listFederalRevenueReferences(input);
|
|
232
|
+
const availableReferences = references.map((item) => item.reference);
|
|
233
|
+
const latest = availableReferences.at(-1);
|
|
234
|
+
if (!latest) {
|
|
235
|
+
throw new ValidationError(
|
|
236
|
+
"Federal Revenue reference discovery failed: no monthly references were found in the public share."
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
if (input.reference) {
|
|
240
|
+
validateFederalRevenueReference(input.reference);
|
|
241
|
+
if (!availableReferences.includes(input.reference)) {
|
|
242
|
+
throw new ValidationError(
|
|
243
|
+
`Federal Revenue reference not found: ${input.reference}. Latest available reference is ${latest}.`,
|
|
244
|
+
{
|
|
245
|
+
requestedReference: input.reference,
|
|
246
|
+
latestAvailableReference: latest,
|
|
247
|
+
availableReferences
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
mode: "explicit",
|
|
253
|
+
selectedReference: input.reference,
|
|
254
|
+
availableReferences
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
if (input.current) {
|
|
258
|
+
const currentReference = getCurrentFederalRevenueReference();
|
|
259
|
+
if (!availableReferences.includes(currentReference)) {
|
|
260
|
+
throw new ValidationError(
|
|
261
|
+
`Federal Revenue current reference is not available yet: ${currentReference}. Latest available reference is ${latest}.`,
|
|
262
|
+
{
|
|
263
|
+
requestedReference: currentReference,
|
|
264
|
+
latestAvailableReference: latest,
|
|
265
|
+
availableReferences
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
mode: "current",
|
|
271
|
+
selectedReference: currentReference,
|
|
272
|
+
availableReferences
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
mode: "latest",
|
|
277
|
+
selectedReference: latest,
|
|
278
|
+
availableReferences
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async function listFederalRevenueFiles(reference, options = {}) {
|
|
282
|
+
validateFederalRevenueReference(reference);
|
|
283
|
+
const result = await propfind([reference], options);
|
|
284
|
+
const files = result.entries.filter(
|
|
285
|
+
(entry) => !entry.isCollection && entry.name.toLowerCase().endsWith(".zip")
|
|
286
|
+
).map((entry) => ({
|
|
287
|
+
name: entry.name,
|
|
288
|
+
href: entry.href,
|
|
289
|
+
downloadUrl: buildUrl(result.baseUrl, [reference, entry.name]),
|
|
290
|
+
...entry.sizeInBytes !== void 0 ? { sizeInBytes: entry.sizeInBytes } : {},
|
|
291
|
+
...entry.lastModified ? { lastModified: entry.lastModified } : {},
|
|
292
|
+
...entry.etag ? { etag: entry.etag } : {}
|
|
293
|
+
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
294
|
+
return {
|
|
295
|
+
files,
|
|
296
|
+
remoteBaseUrl: result.baseUrl
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function buildFederalRevenueDownloadHeaders(options = {}) {
|
|
300
|
+
return {
|
|
301
|
+
Authorization: getAuthHeader(getShareToken(options.shareToken)),
|
|
302
|
+
"User-Agent": options.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
90
306
|
// src/services/config.service.ts
|
|
91
307
|
async function readDatabaseConfig() {
|
|
92
308
|
const raw = await safeReadText(getConfigFilePath());
|
|
@@ -114,12 +330,149 @@ function assertPostgresUrl(url) {
|
|
|
114
330
|
);
|
|
115
331
|
}
|
|
116
332
|
}
|
|
333
|
+
function assertHttpUrl(url, label) {
|
|
334
|
+
let parsed;
|
|
335
|
+
try {
|
|
336
|
+
parsed = new URL(url);
|
|
337
|
+
} catch {
|
|
338
|
+
throw new ValidationError(`${label} is not a valid URL.`, { url });
|
|
339
|
+
}
|
|
340
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
341
|
+
throw new ValidationError(`${label} must use the http or https protocol.`, {
|
|
342
|
+
url
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function assertNonEmpty(value, label) {
|
|
347
|
+
const trimmed = value.trim();
|
|
348
|
+
if (!trimmed) {
|
|
349
|
+
throw new ValidationError(`${label} cannot be empty.`);
|
|
350
|
+
}
|
|
351
|
+
return trimmed;
|
|
352
|
+
}
|
|
353
|
+
function normalizeFederalRevenueConfigKey(key) {
|
|
354
|
+
const normalized = key.trim().toLowerCase();
|
|
355
|
+
if (["share-token", "share_token", "token"].includes(normalized)) {
|
|
356
|
+
return "share-token";
|
|
357
|
+
}
|
|
358
|
+
if (["webdav-url", "webdav_url", "base-url", "base_url", "url"].includes(
|
|
359
|
+
normalized
|
|
360
|
+
)) {
|
|
361
|
+
return "webdav-url";
|
|
362
|
+
}
|
|
363
|
+
if (["user-agent", "user_agent"].includes(normalized)) {
|
|
364
|
+
return "user-agent";
|
|
365
|
+
}
|
|
366
|
+
throw new ValidationError(
|
|
367
|
+
`Unknown Federal Revenue config key: ${key}. Expected share-token, webdav-url, or user-agent.`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
function assignFederalRevenueConfigValue(config, key, value) {
|
|
371
|
+
if (key === "share-token") {
|
|
372
|
+
return {
|
|
373
|
+
...config,
|
|
374
|
+
shareToken: assertNonEmpty(value, "Federal Revenue share token")
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
if (key === "webdav-url") {
|
|
378
|
+
const webdavUrl = assertNonEmpty(value, "Federal Revenue WebDAV URL");
|
|
379
|
+
assertHttpUrl(webdavUrl, "Federal Revenue WebDAV URL");
|
|
380
|
+
return { ...config, webdavUrl };
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
...config,
|
|
384
|
+
userAgent: assertNonEmpty(value, "Federal Revenue user agent")
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function deleteFederalRevenueConfigValue(config, key) {
|
|
388
|
+
const nextConfig = { ...config };
|
|
389
|
+
if (key === "share-token") {
|
|
390
|
+
delete nextConfig.shareToken;
|
|
391
|
+
}
|
|
392
|
+
if (key === "webdav-url") {
|
|
393
|
+
delete nextConfig.webdavUrl;
|
|
394
|
+
}
|
|
395
|
+
if (key === "user-agent") {
|
|
396
|
+
delete nextConfig.userAgent;
|
|
397
|
+
}
|
|
398
|
+
return nextConfig;
|
|
399
|
+
}
|
|
400
|
+
function isFederalRevenueConfigEmpty(config) {
|
|
401
|
+
return !config.shareToken && !config.webdavUrl && !config.userAgent;
|
|
402
|
+
}
|
|
117
403
|
async function setDefaultDbUrl(url) {
|
|
118
404
|
assertPostgresUrl(url);
|
|
119
|
-
|
|
405
|
+
const currentConfig = await readDatabaseConfig();
|
|
406
|
+
await writeDatabaseConfig({ ...currentConfig, defaultDbUrl: url });
|
|
120
407
|
}
|
|
121
408
|
async function resetDefaultDbUrl() {
|
|
122
|
-
await
|
|
409
|
+
const currentConfig = await readDatabaseConfig();
|
|
410
|
+
const nextConfig = { ...currentConfig };
|
|
411
|
+
delete nextConfig.defaultDbUrl;
|
|
412
|
+
await writeDatabaseConfig(nextConfig);
|
|
413
|
+
}
|
|
414
|
+
async function setFederalRevenueConfigValue(key, value) {
|
|
415
|
+
const normalizedKey = normalizeFederalRevenueConfigKey(key);
|
|
416
|
+
const currentConfig = await readDatabaseConfig();
|
|
417
|
+
const federalRevenueConfig = assignFederalRevenueConfigValue(
|
|
418
|
+
currentConfig.federalRevenue ?? {},
|
|
419
|
+
normalizedKey,
|
|
420
|
+
value
|
|
421
|
+
);
|
|
422
|
+
await writeDatabaseConfig({
|
|
423
|
+
...currentConfig,
|
|
424
|
+
federalRevenue: federalRevenueConfig
|
|
425
|
+
});
|
|
426
|
+
return getFederalRevenueEffectiveConfig(federalRevenueConfig);
|
|
427
|
+
}
|
|
428
|
+
async function resetFederalRevenueConfig(key) {
|
|
429
|
+
const currentConfig = await readDatabaseConfig();
|
|
430
|
+
if (!key) {
|
|
431
|
+
const nextConfig2 = { ...currentConfig };
|
|
432
|
+
delete nextConfig2.federalRevenue;
|
|
433
|
+
await writeDatabaseConfig(nextConfig2);
|
|
434
|
+
return getFederalRevenueEffectiveConfig({});
|
|
435
|
+
}
|
|
436
|
+
const normalizedKey = normalizeFederalRevenueConfigKey(key);
|
|
437
|
+
const federalRevenueConfig = deleteFederalRevenueConfigValue(
|
|
438
|
+
currentConfig.federalRevenue ?? {},
|
|
439
|
+
normalizedKey
|
|
440
|
+
);
|
|
441
|
+
const nextConfig = { ...currentConfig };
|
|
442
|
+
if (isFederalRevenueConfigEmpty(federalRevenueConfig)) {
|
|
443
|
+
delete nextConfig.federalRevenue;
|
|
444
|
+
} else {
|
|
445
|
+
nextConfig.federalRevenue = federalRevenueConfig;
|
|
446
|
+
}
|
|
447
|
+
await writeDatabaseConfig(nextConfig);
|
|
448
|
+
return getFederalRevenueEffectiveConfig(federalRevenueConfig);
|
|
449
|
+
}
|
|
450
|
+
function getFederalRevenueEffectiveConfig(config = {}) {
|
|
451
|
+
return {
|
|
452
|
+
webdavUrl: config.webdavUrl ?? DEFAULT_FEDERAL_REVENUE_WEBDAV_URL,
|
|
453
|
+
userAgent: config.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT,
|
|
454
|
+
...config.shareToken ? { shareToken: config.shareToken } : {},
|
|
455
|
+
configured: {
|
|
456
|
+
webdavUrl: Boolean(config.webdavUrl),
|
|
457
|
+
userAgent: Boolean(config.userAgent),
|
|
458
|
+
shareToken: Boolean(config.shareToken)
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
async function readFederalRevenueEffectiveConfig() {
|
|
463
|
+
const currentConfig = await readDatabaseConfig();
|
|
464
|
+
return getFederalRevenueEffectiveConfig(currentConfig.federalRevenue ?? {});
|
|
465
|
+
}
|
|
466
|
+
async function resolveFederalRevenueClientOptions(overrides = {}) {
|
|
467
|
+
const currentConfig = await readDatabaseConfig();
|
|
468
|
+
const effectiveConfig = getFederalRevenueEffectiveConfig(
|
|
469
|
+
currentConfig.federalRevenue ?? {}
|
|
470
|
+
);
|
|
471
|
+
return {
|
|
472
|
+
baseUrl: overrides.baseUrl ?? effectiveConfig.webdavUrl,
|
|
473
|
+
shareToken: overrides.shareToken ?? effectiveConfig.shareToken,
|
|
474
|
+
userAgent: overrides.userAgent ?? effectiveConfig.userAgent
|
|
475
|
+
};
|
|
123
476
|
}
|
|
124
477
|
|
|
125
478
|
// src/services/database.service.ts
|
|
@@ -6717,217 +7070,6 @@ async function showQuarantineRow(id, options) {
|
|
|
6717
7070
|
return record;
|
|
6718
7071
|
}
|
|
6719
7072
|
|
|
6720
|
-
// src/services/federal-revenue/client.ts
|
|
6721
|
-
var DEFAULT_FEDERAL_REVENUE_SHARE_TOKEN = "YggdBLfdninEJX9";
|
|
6722
|
-
var DEFAULT_FEDERAL_REVENUE_WEBDAV_URL = "https://arquivos.receitafederal.gov.br/public.php/webdav";
|
|
6723
|
-
var DEFAULT_FEDERAL_REVENUE_USER_AGENT = "cnpj-db-loader federal-revenue-client";
|
|
6724
|
-
var REFERENCE_PATTERN = /^\d{4}-\d{2}$/;
|
|
6725
|
-
function trimTrailingSlash(value) {
|
|
6726
|
-
return value.replace(/\/+$/g, "");
|
|
6727
|
-
}
|
|
6728
|
-
function normalizeBaseUrl(value) {
|
|
6729
|
-
return trimTrailingSlash(value ?? DEFAULT_FEDERAL_REVENUE_WEBDAV_URL);
|
|
6730
|
-
}
|
|
6731
|
-
function getShareToken(value) {
|
|
6732
|
-
return value ?? DEFAULT_FEDERAL_REVENUE_SHARE_TOKEN;
|
|
6733
|
-
}
|
|
6734
|
-
function encodePathSegment(value) {
|
|
6735
|
-
return encodeURIComponent(value).replace(/%2F/gi, "/");
|
|
6736
|
-
}
|
|
6737
|
-
function decodeXml(value) {
|
|
6738
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
6739
|
-
}
|
|
6740
|
-
function decodeHrefSegment(value) {
|
|
6741
|
-
try {
|
|
6742
|
-
return decodeURIComponent(value);
|
|
6743
|
-
} catch {
|
|
6744
|
-
return value;
|
|
6745
|
-
}
|
|
6746
|
-
}
|
|
6747
|
-
function getAuthHeader(shareToken) {
|
|
6748
|
-
return `Basic ${Buffer.from(`${shareToken}:`).toString("base64")}`;
|
|
6749
|
-
}
|
|
6750
|
-
function buildUrl(baseUrl, segments = []) {
|
|
6751
|
-
if (segments.length === 0) {
|
|
6752
|
-
return `${baseUrl}/`;
|
|
6753
|
-
}
|
|
6754
|
-
return `${baseUrl}/${segments.map(encodePathSegment).join("/")}`;
|
|
6755
|
-
}
|
|
6756
|
-
function extractFirst(block, tagName) {
|
|
6757
|
-
const pattern = new RegExp(
|
|
6758
|
-
`<(?:[a-zA-Z0-9_-]+:)?${tagName}\\b[^>]*>([\\s\\S]*?)<\\/(?:[a-zA-Z0-9_-]+:)?${tagName}>`,
|
|
6759
|
-
"i"
|
|
6760
|
-
);
|
|
6761
|
-
const match = block.match(pattern);
|
|
6762
|
-
return match?.[1] ? decodeXml(match[1].trim()) : void 0;
|
|
6763
|
-
}
|
|
6764
|
-
function isCollectionResponse(block) {
|
|
6765
|
-
return /<(?:[a-zA-Z0-9_-]+:)?collection\b/i.test(block);
|
|
6766
|
-
}
|
|
6767
|
-
function getNameFromHref(href) {
|
|
6768
|
-
const cleanHref = href.split("?")[0] ?? href;
|
|
6769
|
-
const withoutTrailingSlash = cleanHref.replace(/\/+$/g, "");
|
|
6770
|
-
const rawName = withoutTrailingSlash.split("/").pop() ?? withoutTrailingSlash;
|
|
6771
|
-
return decodeHrefSegment(rawName);
|
|
6772
|
-
}
|
|
6773
|
-
function parsePropfindXml(xml) {
|
|
6774
|
-
const responseBlocks = xml.match(
|
|
6775
|
-
/<(?:[a-zA-Z0-9_-]+:)?response\b[\s\S]*?<\/(?:[a-zA-Z0-9_-]+:)?response>/gi
|
|
6776
|
-
);
|
|
6777
|
-
if (!responseBlocks) {
|
|
6778
|
-
return [];
|
|
6779
|
-
}
|
|
6780
|
-
return responseBlocks.map((block) => {
|
|
6781
|
-
const href = extractFirst(block, "href");
|
|
6782
|
-
if (!href) {
|
|
6783
|
-
return void 0;
|
|
6784
|
-
}
|
|
6785
|
-
const size = extractFirst(block, "getcontentlength");
|
|
6786
|
-
const parsedSize = size ? Number.parseInt(size, 10) : void 0;
|
|
6787
|
-
const lastModified = extractFirst(block, "getlastmodified");
|
|
6788
|
-
const etag = extractFirst(block, "getetag");
|
|
6789
|
-
return {
|
|
6790
|
-
href,
|
|
6791
|
-
name: getNameFromHref(href),
|
|
6792
|
-
isCollection: isCollectionResponse(block),
|
|
6793
|
-
...Number.isFinite(parsedSize) ? { sizeInBytes: parsedSize } : {},
|
|
6794
|
-
...lastModified ? { lastModified } : {},
|
|
6795
|
-
...etag ? { etag } : {}
|
|
6796
|
-
};
|
|
6797
|
-
}).filter((entry) => entry !== void 0);
|
|
6798
|
-
}
|
|
6799
|
-
async function propfind(pathSegments, options = {}) {
|
|
6800
|
-
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
6801
|
-
const shareToken = getShareToken(options.shareToken);
|
|
6802
|
-
let response;
|
|
6803
|
-
try {
|
|
6804
|
-
response = await fetch(buildUrl(baseUrl, pathSegments), {
|
|
6805
|
-
method: "PROPFIND",
|
|
6806
|
-
headers: {
|
|
6807
|
-
Accept: "application/xml,text/xml,*/*",
|
|
6808
|
-
Authorization: getAuthHeader(shareToken),
|
|
6809
|
-
Depth: "1",
|
|
6810
|
-
"User-Agent": options.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT
|
|
6811
|
-
}
|
|
6812
|
-
});
|
|
6813
|
-
} catch (error) {
|
|
6814
|
-
throw new ValidationError(
|
|
6815
|
-
`Federal Revenue WebDAV request failed before receiving a response: ${error instanceof Error ? error.message : String(error)}.`,
|
|
6816
|
-
{ baseUrl, pathSegments }
|
|
6817
|
-
);
|
|
6818
|
-
}
|
|
6819
|
-
if (!response.ok) {
|
|
6820
|
-
throw new ValidationError(
|
|
6821
|
-
`Federal Revenue WebDAV request failed with status ${response.status} ${response.statusText}.`,
|
|
6822
|
-
{ status: response.status, statusText: response.statusText }
|
|
6823
|
-
);
|
|
6824
|
-
}
|
|
6825
|
-
const xml = await response.text();
|
|
6826
|
-
return {
|
|
6827
|
-
entries: parsePropfindXml(xml),
|
|
6828
|
-
baseUrl,
|
|
6829
|
-
shareToken
|
|
6830
|
-
};
|
|
6831
|
-
}
|
|
6832
|
-
function validateFederalRevenueReference(reference) {
|
|
6833
|
-
if (!REFERENCE_PATTERN.test(reference)) {
|
|
6834
|
-
throw new ValidationError(
|
|
6835
|
-
`Federal Revenue reference is invalid: ${reference}. Expected YYYY-MM.`
|
|
6836
|
-
);
|
|
6837
|
-
}
|
|
6838
|
-
}
|
|
6839
|
-
function getCurrentFederalRevenueReference(date = /* @__PURE__ */ new Date()) {
|
|
6840
|
-
const year = date.getFullYear();
|
|
6841
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
6842
|
-
return `${year}-${month}`;
|
|
6843
|
-
}
|
|
6844
|
-
async function listFederalRevenueReferences(options = {}) {
|
|
6845
|
-
const result = await propfind([], options);
|
|
6846
|
-
const references = result.entries.filter((entry) => entry.isCollection && REFERENCE_PATTERN.test(entry.name)).map((entry) => ({
|
|
6847
|
-
reference: entry.name,
|
|
6848
|
-
href: entry.href
|
|
6849
|
-
})).sort((left, right) => left.reference.localeCompare(right.reference));
|
|
6850
|
-
return {
|
|
6851
|
-
references,
|
|
6852
|
-
remoteBaseUrl: result.baseUrl
|
|
6853
|
-
};
|
|
6854
|
-
}
|
|
6855
|
-
async function resolveFederalRevenueReference(input = {}) {
|
|
6856
|
-
const { references } = await listFederalRevenueReferences(input);
|
|
6857
|
-
const availableReferences = references.map((item) => item.reference);
|
|
6858
|
-
const latest = availableReferences.at(-1);
|
|
6859
|
-
if (!latest) {
|
|
6860
|
-
throw new ValidationError(
|
|
6861
|
-
"Federal Revenue reference discovery failed: no monthly references were found in the public share."
|
|
6862
|
-
);
|
|
6863
|
-
}
|
|
6864
|
-
if (input.reference) {
|
|
6865
|
-
validateFederalRevenueReference(input.reference);
|
|
6866
|
-
if (!availableReferences.includes(input.reference)) {
|
|
6867
|
-
throw new ValidationError(
|
|
6868
|
-
`Federal Revenue reference not found: ${input.reference}. Latest available reference is ${latest}.`,
|
|
6869
|
-
{
|
|
6870
|
-
requestedReference: input.reference,
|
|
6871
|
-
latestAvailableReference: latest,
|
|
6872
|
-
availableReferences
|
|
6873
|
-
}
|
|
6874
|
-
);
|
|
6875
|
-
}
|
|
6876
|
-
return {
|
|
6877
|
-
mode: "explicit",
|
|
6878
|
-
selectedReference: input.reference,
|
|
6879
|
-
availableReferences
|
|
6880
|
-
};
|
|
6881
|
-
}
|
|
6882
|
-
if (input.current) {
|
|
6883
|
-
const currentReference = getCurrentFederalRevenueReference();
|
|
6884
|
-
if (!availableReferences.includes(currentReference)) {
|
|
6885
|
-
throw new ValidationError(
|
|
6886
|
-
`Federal Revenue current reference is not available yet: ${currentReference}. Latest available reference is ${latest}.`,
|
|
6887
|
-
{
|
|
6888
|
-
requestedReference: currentReference,
|
|
6889
|
-
latestAvailableReference: latest,
|
|
6890
|
-
availableReferences
|
|
6891
|
-
}
|
|
6892
|
-
);
|
|
6893
|
-
}
|
|
6894
|
-
return {
|
|
6895
|
-
mode: "current",
|
|
6896
|
-
selectedReference: currentReference,
|
|
6897
|
-
availableReferences
|
|
6898
|
-
};
|
|
6899
|
-
}
|
|
6900
|
-
return {
|
|
6901
|
-
mode: "latest",
|
|
6902
|
-
selectedReference: latest,
|
|
6903
|
-
availableReferences
|
|
6904
|
-
};
|
|
6905
|
-
}
|
|
6906
|
-
async function listFederalRevenueFiles(reference, options = {}) {
|
|
6907
|
-
validateFederalRevenueReference(reference);
|
|
6908
|
-
const result = await propfind([reference], options);
|
|
6909
|
-
const files = result.entries.filter(
|
|
6910
|
-
(entry) => !entry.isCollection && entry.name.toLowerCase().endsWith(".zip")
|
|
6911
|
-
).map((entry) => ({
|
|
6912
|
-
name: entry.name,
|
|
6913
|
-
href: entry.href,
|
|
6914
|
-
downloadUrl: buildUrl(result.baseUrl, [reference, entry.name]),
|
|
6915
|
-
...entry.sizeInBytes !== void 0 ? { sizeInBytes: entry.sizeInBytes } : {},
|
|
6916
|
-
...entry.lastModified ? { lastModified: entry.lastModified } : {},
|
|
6917
|
-
...entry.etag ? { etag: entry.etag } : {}
|
|
6918
|
-
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
6919
|
-
return {
|
|
6920
|
-
files,
|
|
6921
|
-
remoteBaseUrl: result.baseUrl
|
|
6922
|
-
};
|
|
6923
|
-
}
|
|
6924
|
-
function buildFederalRevenueDownloadHeaders(options = {}) {
|
|
6925
|
-
return {
|
|
6926
|
-
Authorization: getAuthHeader(getShareToken(options.shareToken)),
|
|
6927
|
-
"User-Agent": options.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT
|
|
6928
|
-
};
|
|
6929
|
-
}
|
|
6930
|
-
|
|
6931
7073
|
// src/services/federal-revenue/download.ts
|
|
6932
7074
|
import { createWriteStream } from "fs";
|
|
6933
7075
|
import { mkdir as mkdir5, rename, stat as stat5, unlink } from "fs/promises";
|
|
@@ -9656,7 +9798,6 @@ async function generatePostgresDirectScript(inputPath, options = {}) {
|
|
|
9656
9798
|
export {
|
|
9657
9799
|
AppError,
|
|
9658
9800
|
DEFAULT_FEDERAL_REVENUE_DOWNLOAD_ROOT,
|
|
9659
|
-
DEFAULT_FEDERAL_REVENUE_SHARE_TOKEN,
|
|
9660
9801
|
DEFAULT_FEDERAL_REVENUE_USER_AGENT,
|
|
9661
9802
|
DEFAULT_FEDERAL_REVENUE_WEBDAV_URL,
|
|
9662
9803
|
FEDERAL_REVENUE_CONTROL_DIR,
|
|
@@ -9690,6 +9831,7 @@ export {
|
|
|
9690
9831
|
getAllLayouts,
|
|
9691
9832
|
getCurrentFederalRevenueReference,
|
|
9692
9833
|
getFederalRevenueControlDirectory,
|
|
9834
|
+
getFederalRevenueEffectiveConfig,
|
|
9693
9835
|
getFederalRevenueManifestPath,
|
|
9694
9836
|
getFederalRevenueStatus,
|
|
9695
9837
|
getFederalRevenueSyncLockPath,
|
|
@@ -9706,10 +9848,13 @@ export {
|
|
|
9706
9848
|
materializeImportedData,
|
|
9707
9849
|
prettyJson,
|
|
9708
9850
|
readDatabaseConfig,
|
|
9851
|
+
readFederalRevenueEffectiveConfig,
|
|
9709
9852
|
readFederalRevenueManifest,
|
|
9710
9853
|
resetDefaultDbUrl,
|
|
9854
|
+
resetFederalRevenueConfig,
|
|
9711
9855
|
resolveDatabaseUrl,
|
|
9712
9856
|
resolveDbUrl,
|
|
9857
|
+
resolveFederalRevenueClientOptions,
|
|
9713
9858
|
resolveFederalRevenueReference,
|
|
9714
9859
|
resolveInputMode,
|
|
9715
9860
|
resolveSchemaProfile,
|
|
@@ -9719,6 +9864,7 @@ export {
|
|
|
9719
9864
|
safeWriteText,
|
|
9720
9865
|
sanitizeInputDirectory,
|
|
9721
9866
|
setDefaultDbUrl,
|
|
9867
|
+
setFederalRevenueConfigValue,
|
|
9722
9868
|
showQuarantineRow,
|
|
9723
9869
|
syncFederalRevenueDataset,
|
|
9724
9870
|
testDatabaseConnection,
|