@fin.cx/opendata 3.5.0 → 3.6.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 (54) hide show
  1. package/{npmextra.json → .smartconfig.json} +12 -6
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/classes.businessrecord.js +86 -43
  4. package/dist_ts/classes.handelsregister.d.ts +2 -0
  5. package/dist_ts/classes.handelsregister.js +126 -107
  6. package/dist_ts/classes.jsonldata.js +9 -6
  7. package/dist_ts/classes.main.opendata.d.ts +2 -1
  8. package/dist_ts/classes.main.opendata.js +59 -26
  9. package/dist_ts/index.d.ts +1 -0
  10. package/dist_ts/index.js +2 -1
  11. package/dist_ts/laws/classes.lawrecord.d.ts +24 -0
  12. package/dist_ts/laws/classes.lawrecord.js +181 -0
  13. package/dist_ts/laws/classes.lawservice.d.ts +52 -0
  14. package/dist_ts/laws/classes.lawservice.js +740 -0
  15. package/dist_ts/laws/index.d.ts +3 -0
  16. package/dist_ts/laws/index.js +4 -0
  17. package/dist_ts/laws/interfaces.law.d.ts +35 -0
  18. package/dist_ts/laws/interfaces.law.js +2 -0
  19. package/dist_ts/plugins.d.ts +8 -2
  20. package/dist_ts/plugins.js +14 -3
  21. package/dist_ts/stocks/classes.baseproviderservice.js +8 -8
  22. package/dist_ts/stocks/classes.fundamentalsservice.js +9 -9
  23. package/dist_ts/stocks/classes.stockdataservice.js +23 -23
  24. package/dist_ts/stocks/classes.stockservice.js +9 -9
  25. package/dist_ts/stocks/providers/provider.coingecko.d.ts +1 -1
  26. package/dist_ts/stocks/providers/provider.coingecko.js +45 -39
  27. package/dist_ts/stocks/providers/provider.marketstack.d.ts +3 -3
  28. package/dist_ts/stocks/providers/provider.marketstack.js +20 -16
  29. package/dist_ts/stocks/providers/provider.secedgar.d.ts +1 -1
  30. package/dist_ts/stocks/providers/provider.secedgar.js +22 -22
  31. package/dist_ts/stocks/providers/provider.yahoo.d.ts +2 -2
  32. package/dist_ts/stocks/providers/provider.yahoo.js +14 -13
  33. package/package.json +22 -19
  34. package/readme.md +280 -676
  35. package/ts/00_commitinfo_data.ts +1 -1
  36. package/ts/classes.businessrecord.ts +1 -1
  37. package/ts/classes.handelsregister.ts +45 -20
  38. package/ts/classes.jsonldata.ts +7 -6
  39. package/ts/classes.main.opendata.ts +63 -30
  40. package/ts/index.ts +1 -0
  41. package/ts/laws/classes.lawrecord.ts +81 -0
  42. package/ts/laws/classes.lawservice.ts +1010 -0
  43. package/ts/laws/index.ts +3 -0
  44. package/ts/laws/interfaces.law.ts +48 -0
  45. package/ts/plugins.ts +19 -0
  46. package/ts/stocks/classes.fundamentalsservice.ts +1 -1
  47. package/ts/stocks/classes.stockdataservice.ts +17 -7
  48. package/ts/stocks/classes.stockservice.ts +2 -2
  49. package/ts/stocks/providers/provider.marketstack.ts +9 -7
  50. package/ts/stocks/providers/provider.secedgar.ts +2 -2
  51. package/ts/stocks/providers/provider.yahoo.ts +3 -3
  52. package/dist_ts/classes.germanbusinessdata.d.ts +0 -9
  53. package/dist_ts/classes.germanbusinessdata.js +0 -78
  54. package/readme.plan.md +0 -193
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@fin.cx/opendata',
6
- version: '3.5.0',
6
+ version: '3.6.0',
7
7
  description: 'A comprehensive TypeScript library for accessing business data and real-time financial information. Features include German company data management with MongoDB integration, JSONL bulk processing, automated Handelsregister interactions, and real-time stock market data from multiple providers.'
8
8
  }
@@ -18,7 +18,7 @@ export class BusinessRecord extends plugins.smartdata.SmartDataDbDoc<
18
18
 
19
19
  // INSTANCE
20
20
  @plugins.smartdata.unI()
21
- id: string;
21
+ id!: string;
22
22
 
23
23
  @plugins.smartdata.svDb()
24
24
  data: {
@@ -20,15 +20,44 @@ export class HandelsRegister {
20
20
  this.uniqueDowloadFolder = plugins.path.join(this.downloadDir, plugins.smartunique.uniSimple());
21
21
  }
22
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
+
23
52
  public async start() {
24
53
  // Start the browser
25
- await plugins.smartfile.fs.ensureDir(this.uniqueDowloadFolder);
54
+ await this.resetDownloadFolder();
26
55
  await this.smartbrowserInstance.start();
27
56
  }
28
57
 
29
58
  public async stop() {
30
59
  // Stop the browser
31
- await plugins.smartfile.fs.remove(this.uniqueDowloadFolder);
60
+ await plugins.smartfs.directory(this.uniqueDowloadFolder).recursive().delete();
32
61
  await this.smartbrowserInstance.stop();
33
62
  }
34
63
 
@@ -184,24 +213,16 @@ export class HandelsRegister {
184
213
  throw new Error('Invalid file type');
185
214
  }
186
215
  }, typeArg);
187
-
188
-
189
- await plugins.smartfile.fs.waitForFileToBeReady(this.uniqueDowloadFolder);
190
-
191
- const files = await plugins.smartfile.fs.fileTreeToObject(this.uniqueDowloadFolder, '**/*');
192
- const file = files[0];
193
-
194
- // lets clear the folder for the next download
195
- await plugins.smartfile.fs.ensureEmptyDir(this.uniqueDowloadFolder);
196
- switch (typeArg) {
197
- case 'AD':
198
- await file.rename(`ad.pdf`);
199
- break;
200
- case 'SI':
201
- await file.rename(`si.xml`);
202
- break;
203
- break;
204
- }
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();
205
226
  return file;
206
227
  }
207
228
 
@@ -297,6 +318,10 @@ export class HandelsRegister {
297
318
  */
298
319
  public async getSpecificCompany(companyArg: BusinessRecord['data']['germanParsedRegistration']) {
299
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
+
300
325
  const page = await this.getNewPage();
301
326
  await this.navigateToPage(page, 'Normal search');
302
327
  await page.waitForSelector('#form\\:schlagwoerter', { timeout: 5000 });
@@ -53,14 +53,15 @@ export class JsonlDataProcessor<T> {
53
53
  dataUrlArg = 'https://daten.offeneregister.de/de_companies_ocdata.jsonl.bz2'
54
54
  ) {
55
55
  const done = plugins.smartpromise.defer();
56
- const dataExists = await plugins.smartfile.fs.isDirectory(this.germanBusinessDataDir);
56
+ const dataExists = await plugins.smartfs.directory(this.germanBusinessDataDir).exists();
57
57
  if (!dataExists) {
58
- await plugins.smartfile.fs.ensureDir(this.germanBusinessDataDir);
58
+ await plugins.smartfs.directory(this.germanBusinessDataDir).create();
59
59
  } else {
60
60
  }
61
61
 
62
- const smartarchive = await plugins.smartarchive.SmartArchive.fromArchiveUrl(dataUrlArg);
63
- const jsonlDataStream = await smartarchive.exportToStreamOfStreamFiles();
62
+ const jsonlDataStream = await plugins.smartarchive.SmartArchive.create()
63
+ .url(dataUrlArg)
64
+ .toStreamFiles();
64
65
  let totalRecordsCounter = 0;
65
66
  let nextRest: string = '';
66
67
  jsonlDataStream.pipe(
@@ -74,11 +75,11 @@ export class JsonlDataProcessor<T> {
74
75
  writeFunction: async (chunkArg: Buffer, streamToolsArg) => {
75
76
  const currentString = nextRest + chunkArg.toString();
76
77
  const lines = currentString.split('\n');
77
- nextRest = lines.pop();
78
+ nextRest = lines.pop() ?? '';
78
79
  console.log(`Got another ${lines.length} records.`);
79
80
  const concurrentProcessor = new plugins.smartarray.ConcurrentProcessor<string>(
80
81
  async (line) => {
81
- let entry: T;
82
+ let entry: T | undefined;
82
83
  if (!line) return;
83
84
  try {
84
85
  entry = JSON.parse(line);
@@ -1,7 +1,6 @@
1
1
  import { BusinessRecord } from './classes.businessrecord.js';
2
2
  import { HandelsRegister } from './classes.handelsregister.js';
3
3
  import { JsonlDataProcessor, type SeedEntryType } from './classes.jsonldata.js';
4
- import * as paths from './paths.js';
5
4
  import * as plugins from './plugins.js';
6
5
 
7
6
  export interface IOpenDataConfig {
@@ -11,12 +10,13 @@ export interface IOpenDataConfig {
11
10
  }
12
11
 
13
12
  export class OpenData {
14
- public db: plugins.smartdata.SmartdataDb;
15
- private serviceQenv: plugins.qenv.Qenv;
13
+ public db!: plugins.smartdata.SmartdataDb;
14
+ private localSmartDb?: plugins.smartdb.LocalSmartDb;
16
15
  private config: IOpenDataConfig;
16
+ private started = false;
17
17
 
18
- public jsonLDataProcessor: JsonlDataProcessor<SeedEntryType>;
19
- public handelsregister: HandelsRegister;
18
+ public jsonLDataProcessor!: JsonlDataProcessor<SeedEntryType>;
19
+ public handelsregister!: HandelsRegister;
20
20
 
21
21
  public CBusinessRecord = plugins.smartdata.setDefaultManagerForDoc(this, BusinessRecord);
22
22
 
@@ -28,38 +28,62 @@ export class OpenData {
28
28
  throw new Error('@fin.cx/opendata: All directory paths are required (downloadDir, germanBusinessDataDir, nogitDir).');
29
29
  }
30
30
  this.config = configArg;
31
- this.serviceQenv = new plugins.qenv.Qenv(paths.packageDir, this.config.nogitDir);
32
31
  }
33
32
 
34
33
  public async start() {
34
+ if (this.started) {
35
+ return;
36
+ }
37
+
35
38
  // Ensure configured directories exist
36
- await plugins.smartfile.fs.ensureDir(this.config.nogitDir);
37
- await plugins.smartfile.fs.ensureDir(this.config.downloadDir);
38
- await plugins.smartfile.fs.ensureDir(this.config.germanBusinessDataDir);
39
+ await plugins.smartfs.directory(this.config.nogitDir).create();
40
+ await plugins.smartfs.directory(this.config.downloadDir).create();
41
+ await plugins.smartfs.directory(this.config.germanBusinessDataDir).create();
39
42
 
43
+ this.localSmartDb = new plugins.smartdb.LocalSmartDb({
44
+ folderPath: plugins.path.join(this.config.nogitDir, 'opendata-smartdb'),
45
+ });
46
+
47
+ const connectionInfo = await this.localSmartDb.start();
40
48
  this.db = new plugins.smartdata.SmartdataDb({
41
- mongoDbUrl: await this.serviceQenv.getEnvVarOnDemand('MONGODB_URL'),
42
- mongoDbName: await this.serviceQenv.getEnvVarOnDemand('MONGODB_NAME'),
43
- mongoDbUser: await this.serviceQenv.getEnvVarOnDemand('MONGODB_USER'),
44
- mongoDbPass: await this.serviceQenv.getEnvVarOnDemand('MONGODB_PASS'),
49
+ mongoDbUrl: connectionInfo.connectionUri,
50
+ mongoDbName: 'opendata',
45
51
  });
46
- await this.db.init();
47
- this.jsonLDataProcessor = new JsonlDataProcessor(
48
- this.config.germanBusinessDataDir,
49
- async (entryArg) => {
50
- const businessRecord = new this.CBusinessRecord();
51
- businessRecord.id = await this.CBusinessRecord.getNewId();
52
- businessRecord.data.name = entryArg.name;
53
- businessRecord.data.germanParsedRegistration = {
54
- court: entryArg.all_attributes.registered_office,
55
- number: entryArg.all_attributes._registerNummer,
56
- type: entryArg.all_attributes._registerArt as 'HRA' | 'HRB',
57
- };
58
- await businessRecord.save();
52
+
53
+ try {
54
+ await this.db.init();
55
+ await this.db.mongoDb.collection('_opendata_bootstrap').insertOne({
56
+ createdAt: new Date(),
57
+ });
58
+ await this.db.mongoDb.collection('_opendata_bootstrap').deleteMany({});
59
+
60
+ this.jsonLDataProcessor = new JsonlDataProcessor(
61
+ this.config.germanBusinessDataDir,
62
+ async (entryArg) => {
63
+ const businessRecord = new this.CBusinessRecord();
64
+ businessRecord.id = await this.CBusinessRecord.getNewId();
65
+ businessRecord.data.name = entryArg.name;
66
+ businessRecord.data.germanParsedRegistration = {
67
+ court: entryArg.all_attributes.registered_office,
68
+ number: entryArg.all_attributes._registerNummer,
69
+ type: entryArg.all_attributes._registerArt as 'HRA' | 'HRB',
70
+ };
71
+ await businessRecord.save();
72
+ }
73
+ );
74
+
75
+ this.handelsregister = new HandelsRegister(this, this.config.downloadDir);
76
+ await this.handelsregister.start();
77
+ this.started = true;
78
+ } catch (error) {
79
+ if (this.handelsregister) {
80
+ await this.handelsregister.stop().catch(() => {});
59
81
  }
60
- );
61
- this.handelsregister = new HandelsRegister(this, this.config.downloadDir);
62
- await this.handelsregister.start();
82
+ await this.db.close().catch(() => {});
83
+ await this.localSmartDb.stop().catch(() => {});
84
+ this.localSmartDb = undefined;
85
+ throw error;
86
+ }
63
87
  }
64
88
 
65
89
  public async buildInitialDb() {
@@ -81,7 +105,16 @@ export class OpenData {
81
105
 
82
106
 
83
107
  public async stop() {
108
+ if (!this.started) {
109
+ return;
110
+ }
111
+
112
+ if (this.handelsregister) {
113
+ await this.handelsregister.stop();
114
+ }
84
115
  await this.db.close();
85
- await this.handelsregister.stop();
116
+ await this.localSmartDb?.stop();
117
+ this.localSmartDb = undefined;
118
+ this.started = false;
86
119
  }
87
120
  }
package/ts/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './classes.main.opendata.js';
2
+ export * from './laws/index.js';
2
3
  export * from './stocks/index.js';
@@ -0,0 +1,81 @@
1
+ import * as plugins from '../plugins.js';
2
+ import type { TJurisdiction, TLawSource, TRawLawFormat } from './interfaces.law.js';
3
+
4
+ @plugins.smartdata.Manager()
5
+ export class LawRecord extends plugins.smartdata.SmartDataDbDoc<LawRecord, LawRecord> {
6
+ public static getByLookupKey = async (lookupKeyArg: string) => {
7
+ const lawRecords = await LawRecord.getInstances({
8
+ lookupKey: lookupKeyArg,
9
+ });
10
+ return lawRecords[0];
11
+ };
12
+
13
+ @plugins.smartdata.unI()
14
+ id!: string;
15
+
16
+ @plugins.smartdata.unI()
17
+ @plugins.smartdata.svDb()
18
+ lookupKey!: string;
19
+
20
+ @plugins.smartdata.index()
21
+ @plugins.smartdata.svDb()
22
+ jurisdiction!: TJurisdiction;
23
+
24
+ @plugins.smartdata.index()
25
+ @plugins.smartdata.svDb()
26
+ source!: TLawSource;
27
+
28
+ @plugins.smartdata.searchable()
29
+ @plugins.smartdata.svDb()
30
+ identifier!: string;
31
+
32
+ @plugins.smartdata.searchable()
33
+ @plugins.smartdata.svDb()
34
+ title!: string;
35
+
36
+ @plugins.smartdata.searchable()
37
+ @plugins.smartdata.svDb()
38
+ shortTitle: string = '';
39
+
40
+ @plugins.smartdata.searchable()
41
+ @plugins.smartdata.svDb()
42
+ citation: string = '';
43
+
44
+ @plugins.smartdata.index()
45
+ @plugins.smartdata.svDb()
46
+ type: string = '';
47
+
48
+ @plugins.smartdata.index()
49
+ @plugins.smartdata.svDb()
50
+ language: string = '';
51
+
52
+ @plugins.smartdata.svDb()
53
+ sourceUrl!: string;
54
+
55
+ @plugins.smartdata.svDb()
56
+ rawFormat!: TRawLawFormat;
57
+
58
+ @plugins.smartdata.svDb()
59
+ rawBody!: string;
60
+
61
+ @plugins.smartdata.searchable()
62
+ @plugins.smartdata.svDb()
63
+ text!: string;
64
+
65
+ @plugins.smartdata.index()
66
+ @plugins.smartdata.svDb()
67
+ dateIssued: string = '';
68
+
69
+ @plugins.smartdata.index()
70
+ @plugins.smartdata.svDb()
71
+ lastModified: string = '';
72
+
73
+ @plugins.smartdata.svDb()
74
+ sourceMeta: Record<string, string> = {};
75
+
76
+ @plugins.smartdata.svDb()
77
+ fetchedAt: Date = new Date();
78
+
79
+ @plugins.smartdata.svDb()
80
+ syncedAt: Date = new Date();
81
+ }