@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/dist/cli.js CHANGED
@@ -100,6 +100,222 @@ function getConfigFilePath() {
100
100
  return path2.join(os2.homedir(), ".config", "cnpj-db-loader", "config.json");
101
101
  }
102
102
 
103
+ // src/services/federal-revenue/client.ts
104
+ var DEFAULT_FEDERAL_REVENUE_WEBDAV_URL = "https://arquivos.receitafederal.gov.br/public.php/webdav";
105
+ var DEFAULT_FEDERAL_REVENUE_USER_AGENT = "cnpj-db-loader federal-revenue-client";
106
+ var REFERENCE_PATTERN = /^\d{4}-\d{2}$/;
107
+ function trimTrailingSlash(value) {
108
+ return value.replace(/\/+$/g, "");
109
+ }
110
+ function normalizeBaseUrl(value) {
111
+ return trimTrailingSlash(value ?? DEFAULT_FEDERAL_REVENUE_WEBDAV_URL);
112
+ }
113
+ function getShareToken(value) {
114
+ const shareToken = value?.trim();
115
+ if (!shareToken) {
116
+ throw new ValidationError(
117
+ "Federal Revenue public share token is not configured. Run `cnpj-db-loader federal-revenue config set share-token <token>` or pass --share-token."
118
+ );
119
+ }
120
+ return shareToken;
121
+ }
122
+ function encodePathSegment(value) {
123
+ return encodeURIComponent(value).replace(/%2F/gi, "/");
124
+ }
125
+ function decodeXml(value) {
126
+ return value.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
127
+ }
128
+ function decodeHrefSegment(value) {
129
+ try {
130
+ return decodeURIComponent(value);
131
+ } catch {
132
+ return value;
133
+ }
134
+ }
135
+ function getAuthHeader(shareToken) {
136
+ return `Basic ${Buffer.from(`${shareToken}:`).toString("base64")}`;
137
+ }
138
+ function buildUrl(baseUrl, segments = []) {
139
+ if (segments.length === 0) {
140
+ return `${baseUrl}/`;
141
+ }
142
+ return `${baseUrl}/${segments.map(encodePathSegment).join("/")}`;
143
+ }
144
+ function extractFirst(block, tagName) {
145
+ const pattern = new RegExp(
146
+ `<(?:[a-zA-Z0-9_-]+:)?${tagName}\\b[^>]*>([\\s\\S]*?)<\\/(?:[a-zA-Z0-9_-]+:)?${tagName}>`,
147
+ "i"
148
+ );
149
+ const match = block.match(pattern);
150
+ return match?.[1] ? decodeXml(match[1].trim()) : void 0;
151
+ }
152
+ function isCollectionResponse(block) {
153
+ return /<(?:[a-zA-Z0-9_-]+:)?collection\b/i.test(block);
154
+ }
155
+ function getNameFromHref(href) {
156
+ const cleanHref = href.split("?")[0] ?? href;
157
+ const withoutTrailingSlash = cleanHref.replace(/\/+$/g, "");
158
+ const rawName = withoutTrailingSlash.split("/").pop() ?? withoutTrailingSlash;
159
+ return decodeHrefSegment(rawName);
160
+ }
161
+ function parsePropfindXml(xml) {
162
+ const responseBlocks = xml.match(
163
+ /<(?:[a-zA-Z0-9_-]+:)?response\b[\s\S]*?<\/(?:[a-zA-Z0-9_-]+:)?response>/gi
164
+ );
165
+ if (!responseBlocks) {
166
+ return [];
167
+ }
168
+ return responseBlocks.map((block) => {
169
+ const href = extractFirst(block, "href");
170
+ if (!href) {
171
+ return void 0;
172
+ }
173
+ const size = extractFirst(block, "getcontentlength");
174
+ const parsedSize = size ? Number.parseInt(size, 10) : void 0;
175
+ const lastModified = extractFirst(block, "getlastmodified");
176
+ const etag = extractFirst(block, "getetag");
177
+ return {
178
+ href,
179
+ name: getNameFromHref(href),
180
+ isCollection: isCollectionResponse(block),
181
+ ...Number.isFinite(parsedSize) ? { sizeInBytes: parsedSize } : {},
182
+ ...lastModified ? { lastModified } : {},
183
+ ...etag ? { etag } : {}
184
+ };
185
+ }).filter((entry) => entry !== void 0);
186
+ }
187
+ async function propfind(pathSegments, options = {}) {
188
+ const baseUrl = normalizeBaseUrl(options.baseUrl);
189
+ const shareToken = getShareToken(options.shareToken);
190
+ let response;
191
+ try {
192
+ response = await fetch(buildUrl(baseUrl, pathSegments), {
193
+ method: "PROPFIND",
194
+ headers: {
195
+ Accept: "application/xml,text/xml,*/*",
196
+ Authorization: getAuthHeader(shareToken),
197
+ Depth: "1",
198
+ "User-Agent": options.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT
199
+ }
200
+ });
201
+ } catch (error) {
202
+ throw new ValidationError(
203
+ `Federal Revenue WebDAV request failed before receiving a response: ${error instanceof Error ? error.message : String(error)}.`,
204
+ { baseUrl, pathSegments }
205
+ );
206
+ }
207
+ if (!response.ok) {
208
+ throw new ValidationError(
209
+ `Federal Revenue WebDAV request failed with status ${response.status} ${response.statusText}.`,
210
+ { status: response.status, statusText: response.statusText }
211
+ );
212
+ }
213
+ const xml = await response.text();
214
+ return {
215
+ entries: parsePropfindXml(xml),
216
+ baseUrl,
217
+ shareToken
218
+ };
219
+ }
220
+ function validateFederalRevenueReference(reference) {
221
+ if (!REFERENCE_PATTERN.test(reference)) {
222
+ throw new ValidationError(
223
+ `Federal Revenue reference is invalid: ${reference}. Expected YYYY-MM.`
224
+ );
225
+ }
226
+ }
227
+ function getCurrentFederalRevenueReference(date = /* @__PURE__ */ new Date()) {
228
+ const year = date.getFullYear();
229
+ const month = String(date.getMonth() + 1).padStart(2, "0");
230
+ return `${year}-${month}`;
231
+ }
232
+ async function listFederalRevenueReferences(options = {}) {
233
+ const result = await propfind([], options);
234
+ const references = result.entries.filter((entry) => entry.isCollection && REFERENCE_PATTERN.test(entry.name)).map((entry) => ({
235
+ reference: entry.name,
236
+ href: entry.href
237
+ })).sort((left, right) => left.reference.localeCompare(right.reference));
238
+ return {
239
+ references,
240
+ remoteBaseUrl: result.baseUrl
241
+ };
242
+ }
243
+ async function resolveFederalRevenueReference(input2 = {}) {
244
+ const { references } = await listFederalRevenueReferences(input2);
245
+ const availableReferences = references.map((item) => item.reference);
246
+ const latest = availableReferences.at(-1);
247
+ if (!latest) {
248
+ throw new ValidationError(
249
+ "Federal Revenue reference discovery failed: no monthly references were found in the public share."
250
+ );
251
+ }
252
+ if (input2.reference) {
253
+ validateFederalRevenueReference(input2.reference);
254
+ if (!availableReferences.includes(input2.reference)) {
255
+ throw new ValidationError(
256
+ `Federal Revenue reference not found: ${input2.reference}. Latest available reference is ${latest}.`,
257
+ {
258
+ requestedReference: input2.reference,
259
+ latestAvailableReference: latest,
260
+ availableReferences
261
+ }
262
+ );
263
+ }
264
+ return {
265
+ mode: "explicit",
266
+ selectedReference: input2.reference,
267
+ availableReferences
268
+ };
269
+ }
270
+ if (input2.current) {
271
+ const currentReference = getCurrentFederalRevenueReference();
272
+ if (!availableReferences.includes(currentReference)) {
273
+ throw new ValidationError(
274
+ `Federal Revenue current reference is not available yet: ${currentReference}. Latest available reference is ${latest}.`,
275
+ {
276
+ requestedReference: currentReference,
277
+ latestAvailableReference: latest,
278
+ availableReferences
279
+ }
280
+ );
281
+ }
282
+ return {
283
+ mode: "current",
284
+ selectedReference: currentReference,
285
+ availableReferences
286
+ };
287
+ }
288
+ return {
289
+ mode: "latest",
290
+ selectedReference: latest,
291
+ availableReferences
292
+ };
293
+ }
294
+ async function listFederalRevenueFiles(reference, options = {}) {
295
+ validateFederalRevenueReference(reference);
296
+ const result = await propfind([reference], options);
297
+ const files = result.entries.filter(
298
+ (entry) => !entry.isCollection && entry.name.toLowerCase().endsWith(".zip")
299
+ ).map((entry) => ({
300
+ name: entry.name,
301
+ href: entry.href,
302
+ downloadUrl: buildUrl(result.baseUrl, [reference, entry.name]),
303
+ ...entry.sizeInBytes !== void 0 ? { sizeInBytes: entry.sizeInBytes } : {},
304
+ ...entry.lastModified ? { lastModified: entry.lastModified } : {},
305
+ ...entry.etag ? { etag: entry.etag } : {}
306
+ })).sort((left, right) => left.name.localeCompare(right.name));
307
+ return {
308
+ files,
309
+ remoteBaseUrl: result.baseUrl
310
+ };
311
+ }
312
+ function buildFederalRevenueDownloadHeaders(options = {}) {
313
+ return {
314
+ Authorization: getAuthHeader(getShareToken(options.shareToken)),
315
+ "User-Agent": options.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT
316
+ };
317
+ }
318
+
103
319
  // src/services/config.service.ts
104
320
  async function readDatabaseConfig() {
105
321
  const raw = await safeReadText(getConfigFilePath());
@@ -127,12 +343,149 @@ function assertPostgresUrl(url) {
127
343
  );
128
344
  }
129
345
  }
346
+ function assertHttpUrl(url, label) {
347
+ let parsed;
348
+ try {
349
+ parsed = new URL(url);
350
+ } catch {
351
+ throw new ValidationError(`${label} is not a valid URL.`, { url });
352
+ }
353
+ if (!["http:", "https:"].includes(parsed.protocol)) {
354
+ throw new ValidationError(`${label} must use the http or https protocol.`, {
355
+ url
356
+ });
357
+ }
358
+ }
359
+ function assertNonEmpty(value, label) {
360
+ const trimmed = value.trim();
361
+ if (!trimmed) {
362
+ throw new ValidationError(`${label} cannot be empty.`);
363
+ }
364
+ return trimmed;
365
+ }
366
+ function normalizeFederalRevenueConfigKey(key) {
367
+ const normalized = key.trim().toLowerCase();
368
+ if (["share-token", "share_token", "token"].includes(normalized)) {
369
+ return "share-token";
370
+ }
371
+ if (["webdav-url", "webdav_url", "base-url", "base_url", "url"].includes(
372
+ normalized
373
+ )) {
374
+ return "webdav-url";
375
+ }
376
+ if (["user-agent", "user_agent"].includes(normalized)) {
377
+ return "user-agent";
378
+ }
379
+ throw new ValidationError(
380
+ `Unknown Federal Revenue config key: ${key}. Expected share-token, webdav-url, or user-agent.`
381
+ );
382
+ }
383
+ function assignFederalRevenueConfigValue(config, key, value) {
384
+ if (key === "share-token") {
385
+ return {
386
+ ...config,
387
+ shareToken: assertNonEmpty(value, "Federal Revenue share token")
388
+ };
389
+ }
390
+ if (key === "webdav-url") {
391
+ const webdavUrl = assertNonEmpty(value, "Federal Revenue WebDAV URL");
392
+ assertHttpUrl(webdavUrl, "Federal Revenue WebDAV URL");
393
+ return { ...config, webdavUrl };
394
+ }
395
+ return {
396
+ ...config,
397
+ userAgent: assertNonEmpty(value, "Federal Revenue user agent")
398
+ };
399
+ }
400
+ function deleteFederalRevenueConfigValue(config, key) {
401
+ const nextConfig = { ...config };
402
+ if (key === "share-token") {
403
+ delete nextConfig.shareToken;
404
+ }
405
+ if (key === "webdav-url") {
406
+ delete nextConfig.webdavUrl;
407
+ }
408
+ if (key === "user-agent") {
409
+ delete nextConfig.userAgent;
410
+ }
411
+ return nextConfig;
412
+ }
413
+ function isFederalRevenueConfigEmpty(config) {
414
+ return !config.shareToken && !config.webdavUrl && !config.userAgent;
415
+ }
130
416
  async function setDefaultDbUrl(url) {
131
417
  assertPostgresUrl(url);
132
- await writeDatabaseConfig({ defaultDbUrl: url });
418
+ const currentConfig = await readDatabaseConfig();
419
+ await writeDatabaseConfig({ ...currentConfig, defaultDbUrl: url });
133
420
  }
134
421
  async function resetDefaultDbUrl() {
135
- await writeDatabaseConfig({});
422
+ const currentConfig = await readDatabaseConfig();
423
+ const nextConfig = { ...currentConfig };
424
+ delete nextConfig.defaultDbUrl;
425
+ await writeDatabaseConfig(nextConfig);
426
+ }
427
+ async function setFederalRevenueConfigValue(key, value) {
428
+ const normalizedKey = normalizeFederalRevenueConfigKey(key);
429
+ const currentConfig = await readDatabaseConfig();
430
+ const federalRevenueConfig = assignFederalRevenueConfigValue(
431
+ currentConfig.federalRevenue ?? {},
432
+ normalizedKey,
433
+ value
434
+ );
435
+ await writeDatabaseConfig({
436
+ ...currentConfig,
437
+ federalRevenue: federalRevenueConfig
438
+ });
439
+ return getFederalRevenueEffectiveConfig(federalRevenueConfig);
440
+ }
441
+ async function resetFederalRevenueConfig(key) {
442
+ const currentConfig = await readDatabaseConfig();
443
+ if (!key) {
444
+ const nextConfig2 = { ...currentConfig };
445
+ delete nextConfig2.federalRevenue;
446
+ await writeDatabaseConfig(nextConfig2);
447
+ return getFederalRevenueEffectiveConfig({});
448
+ }
449
+ const normalizedKey = normalizeFederalRevenueConfigKey(key);
450
+ const federalRevenueConfig = deleteFederalRevenueConfigValue(
451
+ currentConfig.federalRevenue ?? {},
452
+ normalizedKey
453
+ );
454
+ const nextConfig = { ...currentConfig };
455
+ if (isFederalRevenueConfigEmpty(federalRevenueConfig)) {
456
+ delete nextConfig.federalRevenue;
457
+ } else {
458
+ nextConfig.federalRevenue = federalRevenueConfig;
459
+ }
460
+ await writeDatabaseConfig(nextConfig);
461
+ return getFederalRevenueEffectiveConfig(federalRevenueConfig);
462
+ }
463
+ function getFederalRevenueEffectiveConfig(config = {}) {
464
+ return {
465
+ webdavUrl: config.webdavUrl ?? DEFAULT_FEDERAL_REVENUE_WEBDAV_URL,
466
+ userAgent: config.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT,
467
+ ...config.shareToken ? { shareToken: config.shareToken } : {},
468
+ configured: {
469
+ webdavUrl: Boolean(config.webdavUrl),
470
+ userAgent: Boolean(config.userAgent),
471
+ shareToken: Boolean(config.shareToken)
472
+ }
473
+ };
474
+ }
475
+ async function readFederalRevenueEffectiveConfig() {
476
+ const currentConfig = await readDatabaseConfig();
477
+ return getFederalRevenueEffectiveConfig(currentConfig.federalRevenue ?? {});
478
+ }
479
+ async function resolveFederalRevenueClientOptions(overrides = {}) {
480
+ const currentConfig = await readDatabaseConfig();
481
+ const effectiveConfig = getFederalRevenueEffectiveConfig(
482
+ currentConfig.federalRevenue ?? {}
483
+ );
484
+ return {
485
+ baseUrl: overrides.baseUrl ?? effectiveConfig.webdavUrl,
486
+ shareToken: overrides.shareToken ?? effectiveConfig.shareToken,
487
+ userAgent: overrides.userAgent ?? effectiveConfig.userAgent
488
+ };
136
489
  }
137
490
 
138
491
  // src/services/database.service.ts
@@ -6685,217 +7038,6 @@ async function showQuarantineRow(id, options) {
6685
7038
  return record;
6686
7039
  }
6687
7040
 
6688
- // src/services/federal-revenue/client.ts
6689
- var DEFAULT_FEDERAL_REVENUE_SHARE_TOKEN = "YggdBLfdninEJX9";
6690
- var DEFAULT_FEDERAL_REVENUE_WEBDAV_URL = "https://arquivos.receitafederal.gov.br/public.php/webdav";
6691
- var DEFAULT_FEDERAL_REVENUE_USER_AGENT = "cnpj-db-loader federal-revenue-client";
6692
- var REFERENCE_PATTERN = /^\d{4}-\d{2}$/;
6693
- function trimTrailingSlash(value) {
6694
- return value.replace(/\/+$/g, "");
6695
- }
6696
- function normalizeBaseUrl(value) {
6697
- return trimTrailingSlash(value ?? DEFAULT_FEDERAL_REVENUE_WEBDAV_URL);
6698
- }
6699
- function getShareToken(value) {
6700
- return value ?? DEFAULT_FEDERAL_REVENUE_SHARE_TOKEN;
6701
- }
6702
- function encodePathSegment(value) {
6703
- return encodeURIComponent(value).replace(/%2F/gi, "/");
6704
- }
6705
- function decodeXml(value) {
6706
- return value.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
6707
- }
6708
- function decodeHrefSegment(value) {
6709
- try {
6710
- return decodeURIComponent(value);
6711
- } catch {
6712
- return value;
6713
- }
6714
- }
6715
- function getAuthHeader(shareToken) {
6716
- return `Basic ${Buffer.from(`${shareToken}:`).toString("base64")}`;
6717
- }
6718
- function buildUrl(baseUrl, segments = []) {
6719
- if (segments.length === 0) {
6720
- return `${baseUrl}/`;
6721
- }
6722
- return `${baseUrl}/${segments.map(encodePathSegment).join("/")}`;
6723
- }
6724
- function extractFirst(block, tagName) {
6725
- const pattern = new RegExp(
6726
- `<(?:[a-zA-Z0-9_-]+:)?${tagName}\\b[^>]*>([\\s\\S]*?)<\\/(?:[a-zA-Z0-9_-]+:)?${tagName}>`,
6727
- "i"
6728
- );
6729
- const match = block.match(pattern);
6730
- return match?.[1] ? decodeXml(match[1].trim()) : void 0;
6731
- }
6732
- function isCollectionResponse(block) {
6733
- return /<(?:[a-zA-Z0-9_-]+:)?collection\b/i.test(block);
6734
- }
6735
- function getNameFromHref(href) {
6736
- const cleanHref = href.split("?")[0] ?? href;
6737
- const withoutTrailingSlash = cleanHref.replace(/\/+$/g, "");
6738
- const rawName = withoutTrailingSlash.split("/").pop() ?? withoutTrailingSlash;
6739
- return decodeHrefSegment(rawName);
6740
- }
6741
- function parsePropfindXml(xml) {
6742
- const responseBlocks = xml.match(
6743
- /<(?:[a-zA-Z0-9_-]+:)?response\b[\s\S]*?<\/(?:[a-zA-Z0-9_-]+:)?response>/gi
6744
- );
6745
- if (!responseBlocks) {
6746
- return [];
6747
- }
6748
- return responseBlocks.map((block) => {
6749
- const href = extractFirst(block, "href");
6750
- if (!href) {
6751
- return void 0;
6752
- }
6753
- const size = extractFirst(block, "getcontentlength");
6754
- const parsedSize = size ? Number.parseInt(size, 10) : void 0;
6755
- const lastModified = extractFirst(block, "getlastmodified");
6756
- const etag = extractFirst(block, "getetag");
6757
- return {
6758
- href,
6759
- name: getNameFromHref(href),
6760
- isCollection: isCollectionResponse(block),
6761
- ...Number.isFinite(parsedSize) ? { sizeInBytes: parsedSize } : {},
6762
- ...lastModified ? { lastModified } : {},
6763
- ...etag ? { etag } : {}
6764
- };
6765
- }).filter((entry) => entry !== void 0);
6766
- }
6767
- async function propfind(pathSegments, options = {}) {
6768
- const baseUrl = normalizeBaseUrl(options.baseUrl);
6769
- const shareToken = getShareToken(options.shareToken);
6770
- let response;
6771
- try {
6772
- response = await fetch(buildUrl(baseUrl, pathSegments), {
6773
- method: "PROPFIND",
6774
- headers: {
6775
- Accept: "application/xml,text/xml,*/*",
6776
- Authorization: getAuthHeader(shareToken),
6777
- Depth: "1",
6778
- "User-Agent": options.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT
6779
- }
6780
- });
6781
- } catch (error) {
6782
- throw new ValidationError(
6783
- `Federal Revenue WebDAV request failed before receiving a response: ${error instanceof Error ? error.message : String(error)}.`,
6784
- { baseUrl, pathSegments }
6785
- );
6786
- }
6787
- if (!response.ok) {
6788
- throw new ValidationError(
6789
- `Federal Revenue WebDAV request failed with status ${response.status} ${response.statusText}.`,
6790
- { status: response.status, statusText: response.statusText }
6791
- );
6792
- }
6793
- const xml = await response.text();
6794
- return {
6795
- entries: parsePropfindXml(xml),
6796
- baseUrl,
6797
- shareToken
6798
- };
6799
- }
6800
- function validateFederalRevenueReference(reference) {
6801
- if (!REFERENCE_PATTERN.test(reference)) {
6802
- throw new ValidationError(
6803
- `Federal Revenue reference is invalid: ${reference}. Expected YYYY-MM.`
6804
- );
6805
- }
6806
- }
6807
- function getCurrentFederalRevenueReference(date = /* @__PURE__ */ new Date()) {
6808
- const year = date.getFullYear();
6809
- const month = String(date.getMonth() + 1).padStart(2, "0");
6810
- return `${year}-${month}`;
6811
- }
6812
- async function listFederalRevenueReferences(options = {}) {
6813
- const result = await propfind([], options);
6814
- const references = result.entries.filter((entry) => entry.isCollection && REFERENCE_PATTERN.test(entry.name)).map((entry) => ({
6815
- reference: entry.name,
6816
- href: entry.href
6817
- })).sort((left, right) => left.reference.localeCompare(right.reference));
6818
- return {
6819
- references,
6820
- remoteBaseUrl: result.baseUrl
6821
- };
6822
- }
6823
- async function resolveFederalRevenueReference(input2 = {}) {
6824
- const { references } = await listFederalRevenueReferences(input2);
6825
- const availableReferences = references.map((item) => item.reference);
6826
- const latest = availableReferences.at(-1);
6827
- if (!latest) {
6828
- throw new ValidationError(
6829
- "Federal Revenue reference discovery failed: no monthly references were found in the public share."
6830
- );
6831
- }
6832
- if (input2.reference) {
6833
- validateFederalRevenueReference(input2.reference);
6834
- if (!availableReferences.includes(input2.reference)) {
6835
- throw new ValidationError(
6836
- `Federal Revenue reference not found: ${input2.reference}. Latest available reference is ${latest}.`,
6837
- {
6838
- requestedReference: input2.reference,
6839
- latestAvailableReference: latest,
6840
- availableReferences
6841
- }
6842
- );
6843
- }
6844
- return {
6845
- mode: "explicit",
6846
- selectedReference: input2.reference,
6847
- availableReferences
6848
- };
6849
- }
6850
- if (input2.current) {
6851
- const currentReference = getCurrentFederalRevenueReference();
6852
- if (!availableReferences.includes(currentReference)) {
6853
- throw new ValidationError(
6854
- `Federal Revenue current reference is not available yet: ${currentReference}. Latest available reference is ${latest}.`,
6855
- {
6856
- requestedReference: currentReference,
6857
- latestAvailableReference: latest,
6858
- availableReferences
6859
- }
6860
- );
6861
- }
6862
- return {
6863
- mode: "current",
6864
- selectedReference: currentReference,
6865
- availableReferences
6866
- };
6867
- }
6868
- return {
6869
- mode: "latest",
6870
- selectedReference: latest,
6871
- availableReferences
6872
- };
6873
- }
6874
- async function listFederalRevenueFiles(reference, options = {}) {
6875
- validateFederalRevenueReference(reference);
6876
- const result = await propfind([reference], options);
6877
- const files = result.entries.filter(
6878
- (entry) => !entry.isCollection && entry.name.toLowerCase().endsWith(".zip")
6879
- ).map((entry) => ({
6880
- name: entry.name,
6881
- href: entry.href,
6882
- downloadUrl: buildUrl(result.baseUrl, [reference, entry.name]),
6883
- ...entry.sizeInBytes !== void 0 ? { sizeInBytes: entry.sizeInBytes } : {},
6884
- ...entry.lastModified ? { lastModified: entry.lastModified } : {},
6885
- ...entry.etag ? { etag: entry.etag } : {}
6886
- })).sort((left, right) => left.name.localeCompare(right.name));
6887
- return {
6888
- files,
6889
- remoteBaseUrl: result.baseUrl
6890
- };
6891
- }
6892
- function buildFederalRevenueDownloadHeaders(options = {}) {
6893
- return {
6894
- Authorization: getAuthHeader(getShareToken(options.shareToken)),
6895
- "User-Agent": options.userAgent ?? DEFAULT_FEDERAL_REVENUE_USER_AGENT
6896
- };
6897
- }
6898
-
6899
7041
  // src/services/federal-revenue/download.ts
6900
7042
  import { createWriteStream } from "fs";
6901
7043
  import { mkdir as mkdir5, rename, stat as stat5, unlink } from "fs/promises";
@@ -9852,6 +9994,28 @@ function printDatabaseConfigSummary(config, logFilePath) {
9852
9994
  );
9853
9995
  console.log(`${theme.muted("Log file:")} ${resolveLogFilePath(logFilePath)}`);
9854
9996
  }
9997
+ function printFederalRevenueConfigSummary(config, logFilePath) {
9998
+ console.log(
9999
+ theme.successLabel("FEDERAL REVENUE"),
10000
+ "Federal Revenue configuration loaded."
10001
+ );
10002
+ console.log(
10003
+ formatKeyValue(
10004
+ "WebDAV URL",
10005
+ `${config.webdavUrl}${config.configured.webdavUrl ? "" : " (default)"}`
10006
+ )
10007
+ );
10008
+ console.log(
10009
+ formatKeyValue(
10010
+ "User agent",
10011
+ `${config.userAgent}${config.configured.userAgent ? "" : " (default)"}`
10012
+ )
10013
+ );
10014
+ console.log(
10015
+ formatKeyValue("Share token", config.shareToken ?? "not configured")
10016
+ );
10017
+ console.log(`${theme.muted("Log file:")} ${resolveLogFilePath(logFilePath)}`);
10018
+ }
9855
10019
  function printDatabaseCleanupSummary(summary, logFilePath) {
9856
10020
  console.log(
9857
10021
  theme.successLabel("DATABASE"),
@@ -11377,8 +11541,21 @@ function applySharedOptions(options, target) {
11377
11541
  if (options.shareToken) {
11378
11542
  target.shareToken = options.shareToken;
11379
11543
  }
11544
+ if (options.userAgent) {
11545
+ target.userAgent = options.userAgent;
11546
+ }
11380
11547
  return target;
11381
11548
  }
11549
+ async function resolveSharedOptions(referenceArgument, options) {
11550
+ const mergedOptions = mergeSharedOptions(referenceArgument, options);
11551
+ const clientOptions = await resolveFederalRevenueClientOptions(mergedOptions);
11552
+ return {
11553
+ ...mergedOptions,
11554
+ ...clientOptions.baseUrl ? { baseUrl: clientOptions.baseUrl } : {},
11555
+ ...clientOptions.shareToken ? { shareToken: clientOptions.shareToken } : {},
11556
+ ...clientOptions.userAgent ? { userAgent: clientOptions.userAgent } : {}
11557
+ };
11558
+ }
11382
11559
  function buildDownloadOptions(options) {
11383
11560
  const downloadOptions = applySharedOptions(
11384
11561
  options,
@@ -11463,6 +11640,9 @@ function registerSharedOptions(command) {
11463
11640
  ).option(
11464
11641
  "--share-token <token>",
11465
11642
  "Override the public Federal Revenue share token."
11643
+ ).option(
11644
+ "--user-agent <value>",
11645
+ "Override the Federal Revenue HTTP user agent."
11466
11646
  );
11467
11647
  }
11468
11648
  function registerDownloadOptions(command) {
@@ -11497,6 +11677,70 @@ function registerFederalRevenueCommands(program) {
11497
11677
  const federalRevenue = program.command("federal-revenue").alias("revenue").description(
11498
11678
  "Check, download, sync, and maintain CNPJ monthly files from the Federal Revenue public share."
11499
11679
  );
11680
+ const config = federalRevenue.command("config").description(
11681
+ "Read, persist, test, or reset Federal Revenue public share settings."
11682
+ );
11683
+ config.command("set").argument(
11684
+ "<key>",
11685
+ "Configuration key: share-token, webdav-url, or user-agent."
11686
+ ).argument("<value>", "Configuration value to persist.").description(
11687
+ "Persist a Federal Revenue setting in the local CNPJ DB Loader config file."
11688
+ ).action(async (key, value) => {
11689
+ const effectiveConfig = await setFederalRevenueConfigValue(key, value);
11690
+ const logFilePath = await writeCommandLog("federal-revenue-config-set", {
11691
+ key,
11692
+ effectiveConfig
11693
+ });
11694
+ printFederalRevenueConfigSummary(effectiveConfig, logFilePath);
11695
+ });
11696
+ config.command("show").description("Show the currently persisted Federal Revenue configuration.").action(async () => {
11697
+ const effectiveConfig = await readFederalRevenueEffectiveConfig();
11698
+ const logFilePath = await writeCommandLog(
11699
+ "federal-revenue-config-show",
11700
+ effectiveConfig
11701
+ );
11702
+ printFederalRevenueConfigSummary(effectiveConfig, logFilePath);
11703
+ });
11704
+ config.command("test").description("Test the configured Federal Revenue WebDAV connection.").action(async () => {
11705
+ const clientOptions = await resolveFederalRevenueClientOptions();
11706
+ const result = await listFederalRevenueReferences(clientOptions);
11707
+ const references = result.references.map((item) => item.reference);
11708
+ const latestReference = references.at(-1) ?? "not found";
11709
+ const logFilePath = await writeCommandLog("federal-revenue-config-test", {
11710
+ remoteBaseUrl: result.remoteBaseUrl,
11711
+ referencesFound: references.length,
11712
+ latestReference
11713
+ });
11714
+ printFederalRevenueConfigSummary(
11715
+ await readFederalRevenueEffectiveConfig(),
11716
+ logFilePath
11717
+ );
11718
+ console.log(
11719
+ `Federal Revenue WebDAV connection succeeded. References found: ${references.length}. Latest reference: ${latestReference}.`
11720
+ );
11721
+ });
11722
+ config.command("reset").argument(
11723
+ "[key]",
11724
+ "Optional key to reset: share-token, webdav-url, or user-agent. When omitted, all Federal Revenue settings are reset."
11725
+ ).option("-f, --force", "Skip the confirmation prompt.").description(
11726
+ "Reset one Federal Revenue setting or all persisted Federal Revenue settings."
11727
+ ).action(async (key, options) => {
11728
+ const target = key ? `Federal Revenue ${key}` : "all Federal Revenue";
11729
+ const confirmed = await confirmFederalRevenueAction(
11730
+ `Reset ${target} configuration?`,
11731
+ options.force
11732
+ );
11733
+ if (!confirmed) {
11734
+ console.log("Federal Revenue config reset cancelled.");
11735
+ return;
11736
+ }
11737
+ const effectiveConfig = await resetFederalRevenueConfig(key);
11738
+ const logFilePath = await writeCommandLog(
11739
+ "federal-revenue-config-reset",
11740
+ { key: key ?? "all", effectiveConfig }
11741
+ );
11742
+ printFederalRevenueConfigSummary(effectiveConfig, logFilePath);
11743
+ });
11500
11744
  registerSharedOptions(
11501
11745
  federalRevenue.command("check").argument(
11502
11746
  "[reference]",
@@ -11506,7 +11750,10 @@ function registerFederalRevenueCommands(program) {
11506
11750
  )
11507
11751
  ).action(
11508
11752
  async (referenceArgument, options) => {
11509
- const resolvedOptions = mergeSharedOptions(referenceArgument, options);
11753
+ const resolvedOptions = await resolveSharedOptions(
11754
+ referenceArgument,
11755
+ options
11756
+ );
11510
11757
  const summary = await checkFederalRevenueDataset(
11511
11758
  applySharedOptions(resolvedOptions, {})
11512
11759
  );
@@ -11526,7 +11773,10 @@ function registerFederalRevenueCommands(program) {
11526
11773
  )
11527
11774
  ).action(
11528
11775
  async (referenceArgument, options) => {
11529
- const resolvedOptions = mergeSharedOptions(referenceArgument, options);
11776
+ const resolvedOptions = await resolveSharedOptions(
11777
+ referenceArgument,
11778
+ options
11779
+ );
11530
11780
  const confirmed = await confirmFederalRevenueAction(
11531
11781
  "Download Federal Revenue CNPJ ZIP files now? Existing completed files are skipped unless --overwrite is used.",
11532
11782
  options.force
@@ -11559,7 +11809,10 @@ function registerFederalRevenueCommands(program) {
11559
11809
  )
11560
11810
  ).action(
11561
11811
  async (referenceArgument, options) => {
11562
- const resolvedOptions = mergeSharedOptions(referenceArgument, options);
11812
+ const resolvedOptions = await resolveSharedOptions(
11813
+ referenceArgument,
11814
+ options
11815
+ );
11563
11816
  const summary = await getFederalRevenueStatus(
11564
11817
  buildStatusOptions({ ...options, ...resolvedOptions })
11565
11818
  );
@@ -11582,7 +11835,10 @@ function registerFederalRevenueCommands(program) {
11582
11835
  )
11583
11836
  ).action(
11584
11837
  async (referenceArgument, options) => {
11585
- const resolvedOptions = mergeSharedOptions(referenceArgument, options);
11838
+ const resolvedOptions = await resolveSharedOptions(
11839
+ referenceArgument,
11840
+ options
11841
+ );
11586
11842
  const confirmed = await confirmFederalRevenueAction(
11587
11843
  "Retry incomplete Federal Revenue files now? Completed files are kept.",
11588
11844
  options.force
@@ -11615,7 +11871,10 @@ function registerFederalRevenueCommands(program) {
11615
11871
  )
11616
11872
  ).action(
11617
11873
  async (referenceArgument, options) => {
11618
- const resolvedOptions = mergeSharedOptions(referenceArgument, options);
11874
+ const resolvedOptions = await resolveSharedOptions(
11875
+ referenceArgument,
11876
+ options
11877
+ );
11619
11878
  const actionLabel = options.all ? "remove the entire selected Federal Revenue reference folder" : options.failed ? "remove failed and partial Federal Revenue files" : "remove Federal Revenue .part files";
11620
11879
  const confirmed = await confirmFederalRevenueAction(
11621
11880
  `This will ${actionLabel}. Continue?`,
@@ -11670,7 +11929,10 @@ function registerFederalRevenueCommands(program) {
11670
11929
  )
11671
11930
  ).action(
11672
11931
  async (referenceArgument, options) => {
11673
- const resolvedOptions = mergeSharedOptions(referenceArgument, options);
11932
+ const resolvedOptions = await resolveSharedOptions(
11933
+ referenceArgument,
11934
+ options
11935
+ );
11674
11936
  const confirmed = await confirmFederalRevenueAction(
11675
11937
  "Run the full Federal Revenue sync now? This downloads files, extracts archives, sanitizes the dataset, and imports it into PostgreSQL.",
11676
11938
  options.force