@fin.cx/opendata 3.6.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.smartconfig.json +23 -20
  2. package/dist_ts/00_commitinfo_data.js +3 -3
  3. package/dist_ts/handelsregister/classes.handelsregisterclient.d.ts +64 -0
  4. package/dist_ts/handelsregister/classes.handelsregisterclient.js +397 -0
  5. package/dist_ts/handelsregister/classes.handelsregistererror.d.ts +56 -0
  6. package/dist_ts/handelsregister/classes.handelsregistererror.js +37 -0
  7. package/dist_ts/handelsregister/classes.portalsession.d.ts +46 -0
  8. package/dist_ts/handelsregister/classes.portalsession.js +160 -0
  9. package/dist_ts/handelsregister/classes.slidingwindowgate.d.ts +32 -0
  10. package/dist_ts/handelsregister/classes.slidingwindowgate.js +55 -0
  11. package/dist_ts/handelsregister/constants.d.ts +13 -0
  12. package/dist_ts/handelsregister/constants.js +19 -0
  13. package/dist_ts/handelsregister/data.codelists.d.ts +29 -0
  14. package/dist_ts/handelsregister/data.codelists.js +616 -0
  15. package/dist_ts/handelsregister/forms.d.ts +27 -0
  16. package/dist_ts/handelsregister/forms.js +77 -0
  17. package/dist_ts/handelsregister/html.d.ts +26 -0
  18. package/dist_ts/handelsregister/html.js +82 -0
  19. package/dist_ts/handelsregister/index.d.ts +9 -0
  20. package/dist_ts/handelsregister/index.js +10 -0
  21. package/dist_ts/handelsregister/interfaces.d.ts +240 -0
  22. package/dist_ts/handelsregister/interfaces.js +2 -0
  23. package/dist_ts/handelsregister/parse.searchresults.d.ts +35 -0
  24. package/dist_ts/handelsregister/parse.searchresults.js +175 -0
  25. package/dist_ts/handelsregister/parse.structuredcontent.d.ts +6 -0
  26. package/dist_ts/handelsregister/parse.structuredcontent.js +306 -0
  27. package/dist_ts/handelsregister/plugins.d.ts +4 -0
  28. package/dist_ts/handelsregister/plugins.js +8 -0
  29. package/dist_ts/handelsregister/references.d.ts +38 -0
  30. package/dist_ts/handelsregister/references.js +146 -0
  31. package/dist_ts/holidays/calendar.d.ts +32 -0
  32. package/dist_ts/holidays/calendar.js +200 -0
  33. package/dist_ts/holidays/classes.holidaycalendarerror.d.ts +14 -0
  34. package/dist_ts/holidays/classes.holidaycalendarerror.js +10 -0
  35. package/dist_ts/holidays/data.holidays.d.ts +46 -0
  36. package/dist_ts/holidays/data.holidays.js +124 -0
  37. package/dist_ts/holidays/index.d.ts +4 -0
  38. package/dist_ts/holidays/index.js +7 -0
  39. package/dist_ts/holidays/interfaces.d.ts +42 -0
  40. package/dist_ts/holidays/interfaces.js +2 -0
  41. package/dist_ts/index.d.ts +0 -2
  42. package/dist_ts/index.js +1 -3
  43. package/dist_ts/plugins.d.ts +2 -20
  44. package/dist_ts/plugins.js +3 -22
  45. package/package.json +40 -37
  46. package/readme.md +250 -119
  47. package/ts/00_commitinfo_data.ts +2 -2
  48. package/ts/handelsregister/classes.handelsregisterclient.ts +474 -0
  49. package/ts/handelsregister/classes.handelsregistererror.ts +82 -0
  50. package/ts/handelsregister/classes.portalsession.ts +191 -0
  51. package/ts/handelsregister/classes.slidingwindowgate.ts +74 -0
  52. package/ts/handelsregister/constants.ts +30 -0
  53. package/ts/handelsregister/data.codelists.ts +640 -0
  54. package/ts/handelsregister/forms.ts +99 -0
  55. package/ts/handelsregister/html.ts +113 -0
  56. package/ts/handelsregister/index.ts +27 -0
  57. package/ts/handelsregister/interfaces.ts +272 -0
  58. package/ts/handelsregister/parse.searchresults.ts +241 -0
  59. package/ts/handelsregister/parse.structuredcontent.ts +338 -0
  60. package/ts/handelsregister/plugins.ts +15 -0
  61. package/ts/handelsregister/references.ts +189 -0
  62. package/ts/holidays/calendar.ts +261 -0
  63. package/ts/holidays/classes.holidaycalendarerror.ts +20 -0
  64. package/ts/holidays/data.holidays.ts +177 -0
  65. package/ts/holidays/index.ts +18 -0
  66. package/ts/holidays/interfaces.ts +87 -0
  67. package/ts/index.ts +0 -2
  68. package/ts/plugins.ts +0 -39
  69. package/dist_ts/classes.businessrecord.d.ts +0 -38
  70. package/dist_ts/classes.businessrecord.js +0 -90
  71. package/dist_ts/classes.handelsregister.d.ts +0 -134
  72. package/dist_ts/classes.handelsregister.js +0 -307
  73. package/dist_ts/classes.jsonldata.d.ts +0 -43
  74. package/dist_ts/classes.jsonldata.js +0 -68
  75. package/dist_ts/classes.main.opendata.d.ts +0 -25
  76. package/dist_ts/classes.main.opendata.js +0 -91
  77. package/dist_ts/laws/classes.lawrecord.d.ts +0 -24
  78. package/dist_ts/laws/classes.lawrecord.js +0 -181
  79. package/dist_ts/laws/classes.lawservice.d.ts +0 -52
  80. package/dist_ts/laws/classes.lawservice.js +0 -740
  81. package/dist_ts/laws/index.d.ts +0 -3
  82. package/dist_ts/laws/index.js +0 -4
  83. package/dist_ts/laws/interfaces.law.d.ts +0 -35
  84. package/dist_ts/laws/interfaces.law.js +0 -2
  85. package/readme.hints.md +0 -89
  86. package/ts/classes.businessrecord.ts +0 -67
  87. package/ts/classes.handelsregister.ts +0 -385
  88. package/ts/classes.jsonldata.ts +0 -114
  89. package/ts/classes.main.opendata.ts +0 -120
  90. package/ts/laws/classes.lawrecord.ts +0 -81
  91. package/ts/laws/classes.lawservice.ts +0 -1010
  92. package/ts/laws/index.ts +0 -3
  93. package/ts/laws/interfaces.law.ts +0 -48
@@ -1,3 +0,0 @@
1
- export * from './interfaces.law.js';
2
- export * from './classes.lawrecord.js';
3
- export * from './classes.lawservice.js';
@@ -1,4 +0,0 @@
1
- export * from './interfaces.law.js';
2
- export * from './classes.lawrecord.js';
3
- export * from './classes.lawservice.js';
4
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9sYXdzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyx3QkFBd0IsQ0FBQztBQUN2QyxjQUFjLHlCQUF5QixDQUFDIn0=
@@ -1,35 +0,0 @@
1
- export type TJurisdiction = 'de' | 'eu' | 'us';
2
- export type TLawSource = 'gesetze-im-internet' | 'eur-lex' | 'law-cornell-lii' | 'govinfo-plaw' | 'govinfo-uscode';
3
- export type TRawLawFormat = 'xml' | 'html' | 'text' | 'json';
4
- export type TUsLawCollection = 'PLAW' | 'USCODE';
5
- export interface ILawServiceConfig {
6
- dbFolderPath?: string;
7
- dbName?: string;
8
- govInfoApiKey?: string;
9
- }
10
- export interface ILawLookupRequest {
11
- jurisdiction: TJurisdiction;
12
- identifier: string;
13
- language?: string;
14
- usCollection?: TUsLawCollection;
15
- forceSync?: boolean;
16
- }
17
- export interface ILawSyncRequest {
18
- jurisdiction: TJurisdiction;
19
- limit?: number;
20
- offset?: number;
21
- language?: string;
22
- govInfoApiKey?: string;
23
- usCollection?: TUsLawCollection;
24
- since?: Date;
25
- }
26
- export interface ILawSearchRequest {
27
- query: string;
28
- jurisdiction?: TJurisdiction;
29
- limit?: number;
30
- }
31
- export interface ILawSyncResult {
32
- jurisdiction: TJurisdiction;
33
- syncedCount: number;
34
- identifiers: string[];
35
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5sYXcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9sYXdzL2ludGVyZmFjZXMubGF3LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIifQ==
package/readme.hints.md DELETED
@@ -1,89 +0,0 @@
1
- # OpenData Project Hints
2
-
3
- ## Stocks Module
4
-
5
- ### Overview
6
- The stocks module provides real-time stock price data and cryptocurrency prices through various provider implementations. Currently supports Yahoo Finance, Marketstack, and CoinGecko with an extensible architecture for additional providers.
7
-
8
- ### Architecture
9
- - **Provider Pattern**: Each stock data source implements the `IStockProvider` interface
10
- - **Service Registry**: `StockPriceService` manages providers with priority-based selection
11
- - **Caching**: Built-in cache with configurable TTL to reduce API calls
12
- - **Fallback Logic**: Automatic failover between providers if one fails
13
-
14
- ### Yahoo Finance Provider Notes
15
- - Uses public API endpoints (no authentication required)
16
- - Two main endpoints:
17
- - `/v8/finance/chart/{ticker}` - Single ticker with full data
18
- - `/v8/finance/spark?symbols={tickers}` - Multiple tickers with basic data
19
- - Response data is in `response.body` when using smartrequest
20
- - Requires User-Agent header to avoid rate limiting
21
-
22
- ### Usage Example
23
- ```typescript
24
- import { StockPriceService, YahooFinanceProvider } from '@fin.cx/opendata';
25
-
26
- const stockService = new StockPriceService({ ttl: 60000 });
27
- const yahooProvider = new YahooFinanceProvider();
28
- stockService.register(yahooProvider);
29
-
30
- const price = await stockService.getPrice({ ticker: 'AAPL' });
31
- console.log(`${price.ticker}: $${price.price}`);
32
- ```
33
-
34
- ### CoinGecko Provider Notes
35
- - Cryptocurrency price provider supporting 13M+ tokens
36
- - Three main endpoints:
37
- - `/simple/price` - Current prices with market data (batch supported)
38
- - `/coins/{id}/market_chart` - Historical and intraday prices with OHLCV
39
- - `/coins/list` - Complete coin list for ticker-to-ID mapping
40
- - **Rate Limiting**:
41
- - Free tier: 5-15 calls/minute (no registration)
42
- - Demo plan: 30 calls/minute, 10,000/month (free with registration)
43
- - Custom rate limiter tracks requests per minute
44
- - **Ticker Resolution**:
45
- - Accepts both ticker symbols (BTC, ETH) and CoinGecko IDs (bitcoin, ethereum)
46
- - Lazy-loads coin list on first ticker resolution
47
- - Caches coin mappings for 24 hours
48
- - IDs with hyphens (wrapped-bitcoin) assumed to be CoinGecko IDs
49
- - **24/7 Markets**: Crypto markets always return `marketState: 'REGULAR'`
50
- - **Optional API Key**: Pass key to constructor for higher rate limits
51
- - Demo plan: `x-cg-demo-api-key` header
52
- - Paid plans: `x-cg-pro-api-key` header
53
- - **Data Granularity**:
54
- - Historical: Daily data for date ranges
55
- - Intraday: Hourly data only (1-90 days based on `days` param)
56
- - Current: Real-time prices with 24h change and volume
57
-
58
- ### Usage Example (Crypto)
59
- ```typescript
60
- import { StockPriceService, CoinGeckoProvider } from '@fin.cx/opendata';
61
-
62
- const stockService = new StockPriceService({ ttl: 30000 });
63
- const coingeckoProvider = new CoinGeckoProvider(); // or new CoinGeckoProvider('api-key')
64
- stockService.register(coingeckoProvider);
65
-
66
- // Using ticker symbol
67
- const btc = await stockService.getPrice({ ticker: 'BTC' });
68
- console.log(`${btc.ticker}: $${btc.price}`);
69
-
70
- // Using CoinGecko ID
71
- const ethereum = await stockService.getPrice({ ticker: 'ethereum' });
72
-
73
- // Batch fetch
74
- const cryptos = await stockService.getPrices({ tickers: ['BTC', 'ETH', 'USDT'] });
75
- ```
76
-
77
- ### Testing
78
- - Tests use real API calls (be mindful of rate limits)
79
- - Mock invalid ticker 'INVALID_TICKER_XYZ' for error testing
80
- - Clear cache between tests to ensure fresh data
81
- - The spark endpoint may return fewer results than requested
82
- - CoinGecko tests may take longer due to rate limiting (wait between requests)
83
-
84
- ### Future Providers
85
- To add a new provider:
86
- 1. Create `ts/stocks/providers/provider.{name}.ts`
87
- 2. Implement the `IStockProvider` interface
88
- 3. Register with `StockPriceService`
89
- 4. No changes needed to existing code
@@ -1,67 +0,0 @@
1
- import * as plugins from './plugins.js';
2
-
3
- @plugins.smartdata.Manager()
4
- export class BusinessRecord extends plugins.smartdata.SmartDataDbDoc<
5
- BusinessRecord,
6
- BusinessRecord
7
- > {
8
- // STATIC
9
- public static getByGermanParsedRegistration = async (parsedGermanRegistrationArg: BusinessRecord['data']['germanParsedRegistration']) => {
10
- const businessRecords = await BusinessRecord.getInstance({
11
- data: {
12
- germanParsedRegistration: parsedGermanRegistrationArg,
13
- }
14
- });
15
- return businessRecords;
16
- };
17
-
18
-
19
- // INSTANCE
20
- @plugins.smartdata.unI()
21
- id!: string;
22
-
23
- @plugins.smartdata.svDb()
24
- data: {
25
- name?: string;
26
- startDate?: string;
27
- endDate?: string;
28
- status?: 'active' | 'liquidating' | 'closed';
29
- address?: string;
30
- postalCode?: string;
31
- city?: string;
32
- country?: string;
33
- phone?: string;
34
- fax?: string;
35
- email?: string;
36
- website?: string;
37
- businessType?: string;
38
- registrationId?: string;
39
- germanParsedRegistration?: {
40
- court?: string;
41
- type?: 'HRA' | 'HRB' | 'GnR' | 'PR' | 'VR' | 'GsR';
42
- number?: string;
43
- };
44
- legalForm?:
45
- | 'GmbH'
46
- | 'GmbH & Co. KG'
47
- | 'AG'
48
- | 'LLC'
49
- | 'LLP'
50
- | 'GmbH & Co. KGaA'
51
- | 'GmbH & Co. KGaA, LLC';
52
- managingDirectors?: string[];
53
- boardOfDirectors?: string[];
54
- supervisoryBoard?: string[];
55
- foundingDate?: string;
56
- capital?: string;
57
- purpose?: string;
58
- lastUpdate?: string;
59
- } = {};
60
-
61
- /**
62
- * validates the record against the Handelregister.
63
- */
64
- public async validate() {
65
- if (!this.data.name) throw new Error('Name is required.');
66
- }
67
- }
@@ -1,385 +0,0 @@
1
- import type { BusinessRecord } from './classes.businessrecord.js';
2
- import type { OpenData } from './classes.main.opendata.js';
3
- import * as plugins from './plugins.js';
4
-
5
- /**
6
- * the HandlesRegister exposed as a class
7
- */
8
- export class HandelsRegister {
9
- private openDataRef: OpenData;
10
- private asyncExecutionStack = new plugins.lik.AsyncExecutionStack();
11
- private downloadDir: string;
12
- private uniqueDowloadFolder: string;
13
-
14
- // Puppeteer wrapper instance
15
- public smartbrowserInstance = new plugins.smartbrowser.SmartBrowser();
16
-
17
- constructor(openDataRef: OpenData, downloadDirArg: string) {
18
- this.openDataRef = openDataRef;
19
- this.downloadDir = downloadDirArg;
20
- this.uniqueDowloadFolder = plugins.path.join(this.downloadDir, plugins.smartunique.uniSimple());
21
- }
22
-
23
- private async resetDownloadFolder() {
24
- await plugins.smartfs.directory(this.uniqueDowloadFolder).recursive().delete().catch(() => {});
25
- await plugins.smartfs.directory(this.uniqueDowloadFolder).create();
26
- }
27
-
28
- private async waitForDownloadedFile() {
29
- for (let attempt = 0; attempt < 120; attempt++) {
30
- const directoryEntries = await plugins.fs.readdir(this.uniqueDowloadFolder);
31
- const fileName = directoryEntries.find(
32
- (entry) => !entry.endsWith('.crdownload') && !entry.endsWith('.tmp')
33
- );
34
-
35
- if (fileName) {
36
- const filePath = plugins.path.join(this.uniqueDowloadFolder, fileName);
37
- const firstStat = await plugins.fs.stat(filePath);
38
- await plugins.smartdelay.delayFor(500);
39
- const secondStat = await plugins.fs.stat(filePath);
40
-
41
- if (firstStat.size === secondStat.size) {
42
- return filePath;
43
- }
44
- }
45
-
46
- await plugins.smartdelay.delayFor(500);
47
- }
48
-
49
- throw new Error('Timed out while waiting for the download to finish.');
50
- }
51
-
52
- public async start() {
53
- // Start the browser
54
- await this.resetDownloadFolder();
55
- await this.smartbrowserInstance.start();
56
- }
57
-
58
- public async stop() {
59
- // Stop the browser
60
- await plugins.smartfs.directory(this.uniqueDowloadFolder).recursive().delete();
61
- await this.smartbrowserInstance.stop();
62
- }
63
-
64
- /**
65
- * Creates a new page and configures it to allow file downloads
66
- * to a predefined path.
67
- */
68
- public getNewPage = async () => {
69
- const page = await this.smartbrowserInstance.headlessBrowser.newPage();
70
-
71
- // 1) Create a DevTools session for this page
72
- const cdpSession = await page.target().createCDPSession();
73
-
74
- // 2) Allow file downloads and set the download path
75
- await cdpSession.send('Page.setDownloadBehavior', {
76
- behavior: 'allow',
77
- downloadPath: this.uniqueDowloadFolder, // <-- Change this to your desired absolute path
78
- });
79
-
80
- // Optionally set viewport and go to page
81
- await page.setViewport({ width: 1920, height: 1080 });
82
- await page.goto('https://www.handelsregister.de/');
83
- return page;
84
- };
85
-
86
- private navigateToPage = async (
87
- pageArg: plugins.smartbrowser.smartpuppeteer.puppeteer.Page,
88
- pageNameArg: string
89
- ) => {
90
- try {
91
- await pageArg.evaluate((pageNameArg2) => {
92
- const elements = Array.from(document.querySelectorAll('.ui-menuitem-text > span'));
93
- const targetElement = elements.find((el) => el.textContent?.trim() === pageNameArg2);
94
- if (targetElement) {
95
- (targetElement as HTMLElement).click();
96
- }
97
- }, pageNameArg);
98
- console.log(`Navigated to the ${pageNameArg} page successfully.`);
99
- } catch (error) {
100
- console.error(`Failed to navigate to the ${pageNameArg} page:`, error);
101
- }
102
- };
103
-
104
- private waitForResults = async (pageArg: plugins.smartbrowser.smartpuppeteer.puppeteer.Page) => {
105
- await pageArg
106
- .waitForSelector('#ergebnissForm\\:selectedSuchErgebnisFormTable_data', {
107
- timeout: 30000,
108
- })
109
- .catch(async (err) => {
110
- await pageArg.screenshot({ path: `${this.downloadDir}/error.png` as `${string}.png` });
111
- throw err;
112
- });
113
-
114
- const businessRecords: BusinessRecord['data'][] = await pageArg.evaluate(() => {
115
- const rows = document.querySelectorAll(
116
- '#ergebnissForm\\:selectedSuchErgebnisFormTable_data > tr'
117
- );
118
- const records: BusinessRecord['data'][] = [];
119
-
120
- rows.forEach((row) => {
121
- const nameElement = row.querySelector('td.ui-panelgrid-cell span.marginLeft20');
122
- const cityElement = row.querySelector('td.ui-panelgrid-cell.sitzSuchErgebnisse span');
123
- const statusElement = row.querySelector('td.ui-panelgrid-cell span.verticalText');
124
- const registrationCourtElement = row.querySelector(
125
- 'td.ui-panelgrid-cell.fontTableNameSize'
126
- );
127
-
128
- const name = nameElement?.textContent?.trim();
129
- const city = cityElement?.textContent?.trim();
130
- const status = statusElement?.textContent?.trim();
131
- const registrationId = registrationCourtElement?.textContent?.trim();
132
-
133
- // Push parsed data into records array
134
- records.push({
135
- name,
136
- city,
137
- registrationId,
138
- businessType: status,
139
- });
140
- });
141
-
142
- return records;
143
- });
144
- return businessRecords;
145
- };
146
-
147
- private clickFindButton = async (pageArg: plugins.smartbrowser.smartpuppeteer.puppeteer.Page, resultsLimitArg: number = 100) => {
148
- try {
149
- // Wait for the button with the text "Find" to appear
150
- await pageArg.waitForSelector('span.ui-button-text.ui-c', { timeout: 5000 });
151
-
152
- // adjust to 100 results per page
153
- await pageArg.select('#form\\:ergebnisseProSeite_input', `${resultsLimitArg}`);
154
-
155
- // Locate and click the button using its text
156
- await pageArg.evaluate(() => {
157
- const buttons = Array.from(document.querySelectorAll('span.ui-button-text.ui-c'));
158
- const targetButton = buttons.find((button) => button.textContent?.trim() === 'Find');
159
- if (targetButton) {
160
- const parentButton = targetButton.closest('button') || targetButton;
161
- (parentButton as HTMLElement).click();
162
- }
163
- });
164
-
165
- console.log('Find button clicked successfully!');
166
- } catch (error) {
167
- console.error('Failed to find or click the "Find" button:', error);
168
- }
169
- };
170
-
171
- private async downloadFile(
172
- pageArg: plugins.smartbrowser.smartpuppeteer.puppeteer.Page,
173
- typeArg: 'SI' | 'AD'
174
- ) {
175
- // Trigger the file download by clicking on the relevant link
176
- await pageArg.evaluate((typeArg2) => {
177
- // Locate the table body
178
- const tableBody = document.querySelector(
179
- '#ergebnissForm\\:selectedSuchErgebnisFormTable_data'
180
- );
181
- if (!tableBody) {
182
- throw new Error('Table body not found');
183
- }
184
-
185
- // Locate the first row
186
- const firstRow = tableBody.querySelector('tr:nth-child(1)');
187
- if (!firstRow) {
188
- throw new Error('First row not found');
189
- }
190
-
191
- // Locate the last cell in the first row
192
- const lastCell = firstRow.querySelector('td:last-child');
193
- if (!lastCell) {
194
- throw new Error('Last cell not found in the first row');
195
- }
196
-
197
- // Locate the download links
198
- const adLink = lastCell.querySelector('a:first-of-type');
199
- const siLink = lastCell.querySelector('a:last-of-type');
200
- if (!siLink) {
201
- throw new Error('SI link not found in the last cell');
202
- }
203
-
204
- // Simulate a click on the last <a> element
205
- switch (typeArg2) {
206
- case 'AD':
207
- (adLink as HTMLElement).click();
208
- break;
209
- case 'SI':
210
- (siLink as HTMLElement).click();
211
- break;
212
- default:
213
- throw new Error('Invalid file type');
214
- }
215
- }, typeArg);
216
- const downloadedFilePath = await this.waitForDownloadedFile();
217
- const renamedFilePath = plugins.path.join(
218
- this.uniqueDowloadFolder,
219
- typeArg === 'AD' ? 'ad.pdf' : 'si.xml'
220
- );
221
- await plugins.fs.rename(downloadedFilePath, renamedFilePath);
222
- const file = await plugins.smartfileFactory.fromFilePath(renamedFilePath);
223
-
224
- // Keep the download folder empty between requests.
225
- await this.resetDownloadFolder();
226
- return file;
227
- }
228
-
229
- /**
230
- * Helper method to parse the German registration string
231
- */
232
- private async parseGermanRegistration(
233
- input: string
234
- ): Promise<BusinessRecord['data']['germanParsedRegistration']> {
235
- // e.g. District court Berlin (Charlottenburg) HRB 123456
236
- const regex =
237
- /District court (\p{L}[\p{L}\s-]*?(?:\s*\([\p{L}\s-]+\))?)\s+(HRA|HRB|GnR|VR|PR|GsR)\s+(\d+)/u;
238
- const match = input.match(regex);
239
-
240
- if (match) {
241
- return {
242
- court: match[1],
243
- type: match[2] as 'HRA' | 'HRB', // Adjust if needed
244
- number: match[3],
245
- };
246
- }
247
- }
248
-
249
- /**
250
- * Search for a company by name and return basic info
251
- */
252
- public async searchCompany(companyNameArg: string, resultsLimitArg: number = 100) {
253
- return this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
254
- const page = await this.getNewPage();
255
- await this.navigateToPage(page, 'Normal search');
256
-
257
- try {
258
- // Wait for the textarea to appear
259
- await page.waitForSelector('#form\\:schlagwoerter', { timeout: 5000 });
260
-
261
- // Enter text into the textarea
262
- const inputText = companyNameArg;
263
- await page.evaluate((text) => {
264
- const textarea = document.querySelector<HTMLTextAreaElement>('#form\\:schlagwoerter');
265
- if (textarea) {
266
- textarea.value = text; // Set the value
267
- // Trigger the change event manually if required
268
- const event = new Event('change', { bubbles: true });
269
- textarea.dispatchEvent(event);
270
- }
271
- }, inputText);
272
-
273
- console.log('Text entered successfully!');
274
- } catch (error) {
275
- console.error('Failed to find or enter text into the textarea:', error);
276
- }
277
-
278
- try {
279
- // Wait for the radio button's label to appear
280
- await page.waitForSelector('label[for="form:schlagwortOptionen:0"]', { timeout: 5000 });
281
-
282
- // Click the label to select the radio button
283
- await page.evaluate(() => {
284
- const label = document.querySelector<HTMLLabelElement>(
285
- 'label[for="form:schlagwortOptionen:0"]'
286
- );
287
- if (label) {
288
- label.click();
289
- }
290
- });
291
-
292
- console.log('Radio button clicked successfully!');
293
- } catch (error) {
294
- console.error('Failed to find or click the radio button:', error);
295
- }
296
-
297
- await this.clickFindButton(page, resultsLimitArg);
298
-
299
- const businessRecords = await this.waitForResults(page);
300
-
301
- // Parse out the registration info
302
- for (const record of businessRecords) {
303
- if (record.registrationId) {
304
- record.germanParsedRegistration = await this.parseGermanRegistration(
305
- record.registrationId
306
- );
307
- }
308
- }
309
-
310
- await page.close();
311
- return businessRecords;
312
- }, 60000);
313
- }
314
-
315
- /**
316
- * Search for a specific company (known register type/number/court),
317
- * then click on an element that triggers a file download.
318
- */
319
- public async getSpecificCompany(companyArg: BusinessRecord['data']['germanParsedRegistration']) {
320
- return this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
321
- if (!companyArg?.type || !companyArg.number || !companyArg.court) {
322
- throw new Error('A complete parsed German registration is required.');
323
- }
324
-
325
- const page = await this.getNewPage();
326
- await this.navigateToPage(page, 'Normal search');
327
- await page.waitForSelector('#form\\:schlagwoerter', { timeout: 5000 });
328
-
329
- // 1) Type of Register (e.g. HRB, HRA, etc.)
330
- await page.waitForSelector('#form\\:registerArt_label');
331
- await page.click('#form\\:registerArt_label');
332
- await page.waitForSelector('#form\\:registerArt_items');
333
- await page.evaluate((type) => {
334
- const options = Array.from(document.querySelectorAll('#form\\:registerArt_items li'));
335
- const targetOption = options.find((option) => option.textContent?.trim() === type);
336
- (targetOption as HTMLElement)?.click();
337
- }, companyArg.type);
338
-
339
- // 2) Register number
340
- await page.waitForSelector('#form\\:registerNummer');
341
- await page.type('#form\\:registerNummer', companyArg.number);
342
-
343
- // 3) Register court
344
- await page.waitForSelector('#form\\:registergericht_label');
345
- await page.click('#form\\:registergericht_label');
346
- await page.waitForSelector('#form\\:registergericht_items');
347
- await page.evaluate((court) => {
348
- const options = Array.from(document.querySelectorAll('#form\\:registergericht_items li'));
349
- const targetOption = options.find((option) => option.textContent?.trim() === court);
350
- (targetOption as HTMLElement)?.click();
351
- }, companyArg.court);
352
-
353
- // Click 'Find'
354
- await this.clickFindButton(page);
355
-
356
- // Optionally grab the results, just for logging
357
- const businessRecords = await this.waitForResults(page);
358
- console.log(businessRecords);
359
-
360
- const files: plugins.smartfile.SmartFile[] = [];
361
-
362
- // download files
363
- files.push(await this.downloadFile(page, 'SI'));
364
- files.push(await this.downloadFile(page, 'AD'));
365
-
366
- // At this point, the file should have been downloaded automatically
367
- // to the path specified by `Page.setDownloadBehavior`
368
- await page.close();
369
-
370
- return {
371
- businessRecords,
372
- files,
373
- };
374
- }, 60000);
375
- }
376
-
377
- /**
378
- * get specific company by full name
379
- */
380
- public async getSpecificCompanyByName(companyNameArg: string) {
381
- const businessRecords = await this.searchCompany(companyNameArg, 1);
382
- const result = this.getSpecificCompany(businessRecords[0].germanParsedRegistration);
383
- return result;
384
- }
385
- }