@fin.cx/opendata 3.6.0 → 4.0.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.
- package/.smartconfig.json +20 -20
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/handelsregister/classes.handelsregisterclient.d.ts +64 -0
- package/dist_ts/handelsregister/classes.handelsregisterclient.js +397 -0
- package/dist_ts/handelsregister/classes.handelsregistererror.d.ts +56 -0
- package/dist_ts/handelsregister/classes.handelsregistererror.js +37 -0
- package/dist_ts/handelsregister/classes.portalsession.d.ts +46 -0
- package/dist_ts/handelsregister/classes.portalsession.js +160 -0
- package/dist_ts/handelsregister/classes.slidingwindowgate.d.ts +32 -0
- package/dist_ts/handelsregister/classes.slidingwindowgate.js +55 -0
- package/dist_ts/handelsregister/constants.d.ts +13 -0
- package/dist_ts/handelsregister/constants.js +19 -0
- package/dist_ts/handelsregister/data.codelists.d.ts +29 -0
- package/dist_ts/handelsregister/data.codelists.js +616 -0
- package/dist_ts/handelsregister/forms.d.ts +27 -0
- package/dist_ts/handelsregister/forms.js +77 -0
- package/dist_ts/handelsregister/html.d.ts +26 -0
- package/dist_ts/handelsregister/html.js +82 -0
- package/dist_ts/handelsregister/index.d.ts +9 -0
- package/dist_ts/handelsregister/index.js +10 -0
- package/dist_ts/handelsregister/interfaces.d.ts +240 -0
- package/dist_ts/handelsregister/interfaces.js +2 -0
- package/dist_ts/handelsregister/parse.searchresults.d.ts +35 -0
- package/dist_ts/handelsregister/parse.searchresults.js +175 -0
- package/dist_ts/handelsregister/parse.structuredcontent.d.ts +6 -0
- package/dist_ts/handelsregister/parse.structuredcontent.js +306 -0
- package/dist_ts/handelsregister/plugins.d.ts +4 -0
- package/dist_ts/handelsregister/plugins.js +8 -0
- package/dist_ts/handelsregister/references.d.ts +34 -0
- package/dist_ts/handelsregister/references.js +76 -0
- package/dist_ts/index.d.ts +0 -2
- package/dist_ts/index.js +1 -3
- package/dist_ts/plugins.d.ts +2 -20
- package/dist_ts/plugins.js +3 -22
- package/package.json +33 -37
- package/readme.md +159 -126
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/handelsregister/classes.handelsregisterclient.ts +474 -0
- package/ts/handelsregister/classes.handelsregistererror.ts +82 -0
- package/ts/handelsregister/classes.portalsession.ts +191 -0
- package/ts/handelsregister/classes.slidingwindowgate.ts +74 -0
- package/ts/handelsregister/constants.ts +30 -0
- package/ts/handelsregister/data.codelists.ts +640 -0
- package/ts/handelsregister/forms.ts +99 -0
- package/ts/handelsregister/html.ts +113 -0
- package/ts/handelsregister/index.ts +27 -0
- package/ts/handelsregister/interfaces.ts +272 -0
- package/ts/handelsregister/parse.searchresults.ts +241 -0
- package/ts/handelsregister/parse.structuredcontent.ts +338 -0
- package/ts/handelsregister/plugins.ts +15 -0
- package/ts/handelsregister/references.ts +115 -0
- package/ts/index.ts +0 -2
- package/ts/plugins.ts +0 -39
- package/dist_ts/classes.businessrecord.d.ts +0 -38
- package/dist_ts/classes.businessrecord.js +0 -90
- package/dist_ts/classes.handelsregister.d.ts +0 -134
- package/dist_ts/classes.handelsregister.js +0 -307
- package/dist_ts/classes.jsonldata.d.ts +0 -43
- package/dist_ts/classes.jsonldata.js +0 -68
- package/dist_ts/classes.main.opendata.d.ts +0 -25
- package/dist_ts/classes.main.opendata.js +0 -91
- package/dist_ts/laws/classes.lawrecord.d.ts +0 -24
- package/dist_ts/laws/classes.lawrecord.js +0 -181
- package/dist_ts/laws/classes.lawservice.d.ts +0 -52
- package/dist_ts/laws/classes.lawservice.js +0 -740
- package/dist_ts/laws/index.d.ts +0 -3
- package/dist_ts/laws/index.js +0 -4
- package/dist_ts/laws/interfaces.law.d.ts +0 -35
- package/dist_ts/laws/interfaces.law.js +0 -2
- package/readme.hints.md +0 -89
- package/ts/classes.businessrecord.ts +0 -67
- package/ts/classes.handelsregister.ts +0 -385
- package/ts/classes.jsonldata.ts +0 -114
- package/ts/classes.main.opendata.ts +0 -120
- package/ts/laws/classes.lawrecord.ts +0 -81
- package/ts/laws/classes.lawservice.ts +0 -1010
- package/ts/laws/index.ts +0 -3
- package/ts/laws/interfaces.law.ts +0 -48
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { registerTypes } from './constants.js';
|
|
2
|
+
import { registerCourts } from './data.codelists.js';
|
|
3
|
+
const collapse = (textArg) => textArg.replace(/\s+/g, ' ').trim();
|
|
4
|
+
const courtKey = (textArg) => collapse(textArg)
|
|
5
|
+
.replace(/^(?:amtsgericht|registergericht|ag)\s+/i, '')
|
|
6
|
+
.toLocaleLowerCase('de-DE');
|
|
7
|
+
/** the alternative name the portal puts in brackets: `Berlin (Charlottenburg)` → `charlottenburg` */
|
|
8
|
+
const courtAliasKey = (nameArg) => {
|
|
9
|
+
const bracket = /\(([^()]+)\)\s*$/.exec(nameArg);
|
|
10
|
+
return bracket ? courtKey(bracket[1]) : undefined;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Finds a register court by its code or its name: `H1101`, `Bremen`, `Amtsgericht Bremen`,
|
|
14
|
+
* `Berlin (Charlottenburg)` or `Charlottenburg`. Returns undefined unless exactly one court fits.
|
|
15
|
+
*/
|
|
16
|
+
export const findRegisterCourt = (codeOrNameArg, courtsArg = registerCourts) => {
|
|
17
|
+
const text = collapse(codeOrNameArg);
|
|
18
|
+
if (!text) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const byCode = courtsArg.find((courtArg) => courtArg.code.toUpperCase() === text.toUpperCase());
|
|
22
|
+
if (byCode) {
|
|
23
|
+
return byCode;
|
|
24
|
+
}
|
|
25
|
+
const key = courtKey(text);
|
|
26
|
+
const byName = courtsArg.filter((courtArg) => courtKey(courtArg.name) === key);
|
|
27
|
+
if (byName.length === 1) {
|
|
28
|
+
return byName[0];
|
|
29
|
+
}
|
|
30
|
+
if (byName.length > 1) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const byAlias = courtsArg.filter((courtArg) => courtAliasKey(courtArg.name) === key);
|
|
34
|
+
return byAlias.length === 1 ? byAlias[0] : undefined;
|
|
35
|
+
};
|
|
36
|
+
/** returns the canonical register type for `hrb`, `HRB`, `gnr` …, or undefined */
|
|
37
|
+
export const toRegisterType = (textArg) => registerTypes.find((typeArg) => typeArg.toLowerCase() === collapse(textArg).toLowerCase());
|
|
38
|
+
/** splits `35230 HB`, `35230HB` or `35230` into digits and location suffix */
|
|
39
|
+
export const parseRegisterNumber = (textArg) => {
|
|
40
|
+
const match = /^(\d{1,9})\s*([A-Za-zÄÖÜäöü]{1,5})?$/.exec(collapse(textArg));
|
|
41
|
+
if (!match) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
return match[2] ? { digits: match[1], suffix: match[2].toLocaleUpperCase('de-DE') } : { digits: match[1] };
|
|
45
|
+
};
|
|
46
|
+
export const formatRegisterNumber = (partsArg) => partsArg.suffix ? `${partsArg.digits} ${partsArg.suffix}` : partsArg.digits;
|
|
47
|
+
/**
|
|
48
|
+
* Reads a register reference as businesses print it: `Amtsgericht Bremen HRB 35230 HB`,
|
|
49
|
+
* `AG Bremen, HRB 35230`, `HRB 35230 HB`. Returns undefined for text it cannot read. A court name
|
|
50
|
+
* that fits no register court leaves `court` absent and keeps the text in `courtText`.
|
|
51
|
+
*/
|
|
52
|
+
export const parseRegisterReferenceText = (textArg, courtsArg = registerCourts) => {
|
|
53
|
+
const text = collapse(textArg);
|
|
54
|
+
const match = /^(.*?)[\s,;:]*\b(HRA|HRB|GnR|PR|VR|GsR)\b\.?\s*(?:Nr\.?\s*)?(\d{1,9}(?:\s*[A-Za-zÄÖÜäöü]{1,5})?)(?=\s|$)(?:\s+früher\s+Amtsgericht\s+.+)?$/i.exec(text);
|
|
55
|
+
if (!match) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const type = toRegisterType(match[2]);
|
|
59
|
+
const number = parseRegisterNumber(match[3]);
|
|
60
|
+
if (!type || !number) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
const courtText = collapse(match[1].replace(/[,;:]+$/, ''));
|
|
64
|
+
const result = { type, number: formatRegisterNumber(number) };
|
|
65
|
+
if (courtText) {
|
|
66
|
+
result.courtText = courtText;
|
|
67
|
+
const court = findRegisterCourt(courtText, courtsArg);
|
|
68
|
+
if (court) {
|
|
69
|
+
result.court = court;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
74
|
+
/** `Amtsgericht Bremen HRB 35230 HB` */
|
|
75
|
+
export const formatRegisterReference = (referenceArg) => `Amtsgericht ${referenceArg.court.name} ${referenceArg.type} ${referenceArg.number}`;
|
|
76
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVmZXJlbmNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2hhbmRlbHNyZWdpc3Rlci9yZWZlcmVuY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMvQyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFvQnJELE1BQU0sUUFBUSxHQUFHLENBQUMsT0FBZSxFQUFVLEVBQUUsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUVsRixNQUFNLFFBQVEsR0FBRyxDQUFDLE9BQWUsRUFBVSxFQUFFLENBQzNDLFFBQVEsQ0FBQyxPQUFPLENBQUM7S0FDZCxPQUFPLENBQUMseUNBQXlDLEVBQUUsRUFBRSxDQUFDO0tBQ3RELGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBRWhDLHFHQUFxRztBQUNyRyxNQUFNLGFBQWEsR0FBRyxDQUFDLE9BQWUsRUFBc0IsRUFBRTtJQUM1RCxNQUFNLE9BQU8sR0FBRyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDakQsT0FBTyxPQUFPLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0FBQ3BELENBQUMsQ0FBQztBQUVGOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLENBQy9CLGFBQXFCLEVBQ3JCLFlBQXVDLGNBQWMsRUFDekIsRUFBRTtJQUM5QixNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDckMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ1YsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUNELE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxJ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
|
package/dist_ts/index.d.ts
CHANGED
package/dist_ts/index.js
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
export * from './classes.main.opendata.js';
|
|
2
|
-
export * from './laws/index.js';
|
|
3
1
|
export * from './stocks/index.js';
|
|
4
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLG1CQUFtQixDQUFDIn0=
|
package/dist_ts/plugins.d.ts
CHANGED
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
1
|
import * as path from 'node:path';
|
|
3
|
-
export {
|
|
4
|
-
import * as lik from '@push.rocks/lik';
|
|
2
|
+
export { path, };
|
|
5
3
|
import * as qenv from '@push.rocks/qenv';
|
|
6
|
-
import * as smartarchive from '@push.rocks/smartarchive';
|
|
7
|
-
import * as smartarray from '@push.rocks/smartarray';
|
|
8
|
-
import * as smartbrowser from '@push.rocks/smartbrowser';
|
|
9
|
-
import * as smartdata from '@push.rocks/smartdata';
|
|
10
4
|
import * as smartdelay from '@push.rocks/smartdelay';
|
|
11
|
-
import * as smartfile from '@push.rocks/smartfile';
|
|
12
|
-
import { SmartFs } from '@push.rocks/smartfs';
|
|
13
|
-
import * as smartlog from '@push.rocks/smartlog';
|
|
14
5
|
import * as smartpath from '@push.rocks/smartpath';
|
|
15
|
-
import * as smartpromise from '@push.rocks/smartpromise';
|
|
16
6
|
import * as smartrequest from '@push.rocks/smartrequest';
|
|
17
|
-
import * as smartdb from '@push.rocks/smartdb';
|
|
18
|
-
import * as smartstream from '@push.rocks/smartstream';
|
|
19
|
-
import * as smartunique from '@push.rocks/smartunique';
|
|
20
|
-
import * as smartxml from '@push.rocks/smartxml';
|
|
21
|
-
declare const smartfs: SmartFs;
|
|
22
|
-
declare const smartfileFactory: smartfile.SmartFileFactory;
|
|
23
7
|
declare const getErrorMessage: (error: unknown) => string;
|
|
24
|
-
export { getErrorMessage,
|
|
25
|
-
import * as tsclass from '@tsclass/tsclass';
|
|
26
|
-
export { tsclass, };
|
|
8
|
+
export { getErrorMessage, qenv, smartdelay, smartpath, smartrequest, };
|
package/dist_ts/plugins.js
CHANGED
|
@@ -1,35 +1,16 @@
|
|
|
1
1
|
// node native scope
|
|
2
|
-
import * as fs from 'node:fs/promises';
|
|
3
2
|
import * as path from 'node:path';
|
|
4
|
-
export {
|
|
3
|
+
export { path, };
|
|
5
4
|
// @push.rocks scope
|
|
6
|
-
import * as lik from '@push.rocks/lik';
|
|
7
5
|
import * as qenv from '@push.rocks/qenv';
|
|
8
|
-
import * as smartarchive from '@push.rocks/smartarchive';
|
|
9
|
-
import * as smartarray from '@push.rocks/smartarray';
|
|
10
|
-
import * as smartbrowser from '@push.rocks/smartbrowser';
|
|
11
|
-
import * as smartdata from '@push.rocks/smartdata';
|
|
12
6
|
import * as smartdelay from '@push.rocks/smartdelay';
|
|
13
|
-
import * as smartfile from '@push.rocks/smartfile';
|
|
14
|
-
import { SmartFs, SmartFsProviderNode } from '@push.rocks/smartfs';
|
|
15
|
-
import * as smartlog from '@push.rocks/smartlog';
|
|
16
7
|
import * as smartpath from '@push.rocks/smartpath';
|
|
17
|
-
import * as smartpromise from '@push.rocks/smartpromise';
|
|
18
8
|
import * as smartrequest from '@push.rocks/smartrequest';
|
|
19
|
-
import * as smartdb from '@push.rocks/smartdb';
|
|
20
|
-
import * as smartstream from '@push.rocks/smartstream';
|
|
21
|
-
import * as smartunique from '@push.rocks/smartunique';
|
|
22
|
-
import * as smartxml from '@push.rocks/smartxml';
|
|
23
|
-
const smartfs = new SmartFs(new SmartFsProviderNode());
|
|
24
|
-
const smartfileFactory = new smartfile.SmartFileFactory(smartfs);
|
|
25
9
|
const getErrorMessage = (error) => {
|
|
26
10
|
if (error instanceof Error) {
|
|
27
11
|
return error.message;
|
|
28
12
|
}
|
|
29
13
|
return String(error);
|
|
30
14
|
};
|
|
31
|
-
export { getErrorMessage,
|
|
32
|
-
|
|
33
|
-
import * as tsclass from '@tsclass/tsclass';
|
|
34
|
-
export { tsclass, };
|
|
35
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDdkMsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFFbEMsT0FBTyxFQUNMLEVBQUUsRUFDRixJQUFJLEdBQ0wsQ0FBQTtBQUVELG9CQUFvQjtBQUNwQixPQUFPLEtBQUssR0FBRyxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZDLE9BQU8sS0FBSyxJQUFJLE1BQU0sa0JBQWtCLENBQUM7QUFDekMsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBQ3JELE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBQ3JELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxFQUFFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ25FLE9BQU8sS0FBSyxRQUFRLE1BQU0sc0JBQXNCLENBQUM7QUFDakQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxLQUFLLE9BQU8sTUFBTSxxQkFBcUIsQ0FBQztBQUMvQyxPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFDdkQsT0FBTyxLQUFLLFFBQVEsTUFBTSxzQkFBc0IsQ0FBQztBQUVqRCxNQUFNLE9BQU8sR0FBRyxJQUFJLE9BQU8sQ0FBQyxJQUFJLG1CQUFtQixFQUFFLENBQUMsQ0FBQztBQUN2RCxNQUFNLGdCQUFnQixHQUFHLElBQUksU0FBUyxDQUFDLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBRWpFLE1BQU0sZUFBZSxHQUFHLENBQUMsS0FBYyxFQUFFLEVBQUU7SUFDekMsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7UUFDM0IsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUN2QixDQUFDLENBQUM7QUFFRixPQUFPLEVBQ0wsZUFBZSxFQUNmLEdBQUcsRUFDSCxJQUFJLEVBQ0osWUFBWSxFQUNaLFVBQVUsRUFDVixZQUFZLEVBQ1osT0FBTyxFQUNQLFNBQVMsRUFDVCxVQUFVLEVBQ1YsU0FBUyxFQUNULGdCQUFnQixFQUNoQixPQUFPLEVBQ1AsUUFBUSxFQUNSLFNBQVMsRUFDVCxZQUFZLEVBQ1osWUFBWSxFQUNaLFdBQVcsRUFDWCxXQUFXLEVBQ1gsUUFBUSxHQUNULENBQUE7QUFFRCxpQkFBaUI7QUFDakIsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUU1QyxPQUFPLEVBQ0wsT0FBTyxHQUNSLENBQUEifQ==
|
|
15
|
+
export { getErrorMessage, qenv, smartdelay, smartpath, smartrequest, };
|
|
16
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBRWxDLE9BQU8sRUFDTCxJQUFJLEdBQ0wsQ0FBQTtBQUVELG9CQUFvQjtBQUNwQixPQUFPLEtBQUssSUFBSSxNQUFNLGtCQUFrQixDQUFDO0FBQ3pDLE9BQU8sS0FBSyxVQUFVLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBRXpELE1BQU0sZUFBZSxHQUFHLENBQUMsS0FBYyxFQUFFLEVBQUU7SUFDekMsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7UUFDM0IsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUN2QixDQUFDLENBQUM7QUFFRixPQUFPLEVBQ0wsZUFBZSxFQUNmLElBQUksRUFDSixVQUFVLEVBQ1YsU0FBUyxFQUNULFlBQVksR0FDYixDQUFBIn0=
|
package/package.json
CHANGED
|
@@ -1,44 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fin.cx/opendata",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Open business and financial data for TypeScript: register lookups on the German register portal (handelsregister.de) without storage, and stock and crypto prices and fundamentals from several providers.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
7
7
|
"typings": "dist_ts/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist_ts/index.d.ts",
|
|
11
|
+
"import": "./dist_ts/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./handelsregister": {
|
|
14
|
+
"types": "./dist_ts/handelsregister/index.d.ts",
|
|
15
|
+
"import": "./dist_ts/handelsregister/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
8
19
|
"type": "module",
|
|
9
20
|
"author": "Task Venture Capital GmbH",
|
|
10
21
|
"license": "MIT",
|
|
11
|
-
"scripts": {
|
|
12
|
-
"test": "(tstest test/ --verbose)",
|
|
13
|
-
"build": "(tsbuild)",
|
|
14
|
-
"buildDocs": "(tsdoc)"
|
|
15
|
-
},
|
|
16
22
|
"devDependencies": {
|
|
17
|
-
"@git.zone/tsbuild": "^
|
|
18
|
-
"@git.zone/tsbundle": "^2.
|
|
19
|
-
"@git.zone/tsrun": "^
|
|
20
|
-
"@git.zone/tstest": "^
|
|
21
|
-
"@types/node": "
|
|
23
|
+
"@git.zone/tsbuild": "^5.0.0",
|
|
24
|
+
"@git.zone/tsbundle": "^2.15.0",
|
|
25
|
+
"@git.zone/tsrun": "^3.0.0",
|
|
26
|
+
"@git.zone/tstest": "^6.2.0",
|
|
27
|
+
"@types/node": "^26.6.1"
|
|
22
28
|
},
|
|
23
29
|
"dependencies": {
|
|
24
|
-
"@push.rocks/lik": "^6.2.2",
|
|
25
30
|
"@push.rocks/qenv": "^6.1.3",
|
|
26
|
-
"@push.rocks/smartarchive": "^5.2.1",
|
|
27
|
-
"@push.rocks/smartarray": "^1.1.0",
|
|
28
|
-
"@push.rocks/smartbrowser": "^2.0.8",
|
|
29
|
-
"@push.rocks/smartdata": "^7.1.7",
|
|
30
|
-
"@push.rocks/smartdb": "^2.7.0",
|
|
31
31
|
"@push.rocks/smartdelay": "^3.0.5",
|
|
32
|
-
"@push.rocks/smartfile": "^13.1.2",
|
|
33
|
-
"@push.rocks/smartfs": "^1.5.0",
|
|
34
|
-
"@push.rocks/smartlog": "^3.1.10",
|
|
35
32
|
"@push.rocks/smartpath": "^6.0.0",
|
|
36
|
-
"@push.rocks/smartpromise": "^4.2.3",
|
|
37
33
|
"@push.rocks/smartrequest": "^5.0.1",
|
|
38
|
-
"@push.rocks/
|
|
39
|
-
"
|
|
40
|
-
"@push.rocks/smartxml": "^2.0.0",
|
|
41
|
-
"@tsclass/tsclass": "^9.3.0"
|
|
34
|
+
"@push.rocks/smartxml": "^2.1.0",
|
|
35
|
+
"parse5": "^8.0.1"
|
|
42
36
|
},
|
|
43
37
|
"repository": {
|
|
44
38
|
"type": "git",
|
|
@@ -68,17 +62,19 @@
|
|
|
68
62
|
"open data",
|
|
69
63
|
"German companies",
|
|
70
64
|
"business data",
|
|
71
|
-
"MongoDB",
|
|
72
|
-
"JSONL",
|
|
73
|
-
"bulk processing",
|
|
74
|
-
"data management",
|
|
75
|
-
"automation",
|
|
76
|
-
"browser automation",
|
|
77
65
|
"Handelsregister",
|
|
78
|
-
"
|
|
79
|
-
"
|
|
66
|
+
"register portal",
|
|
67
|
+
"commercial register",
|
|
68
|
+
"XJustiz",
|
|
80
69
|
"business registry",
|
|
81
|
-
"data retrieval"
|
|
70
|
+
"data retrieval",
|
|
71
|
+
"stock prices",
|
|
72
|
+
"cryptocurrency",
|
|
73
|
+
"fundamentals"
|
|
82
74
|
],
|
|
83
|
-
"
|
|
84
|
-
|
|
75
|
+
"scripts": {
|
|
76
|
+
"test": "(tstest test/ --verbose)",
|
|
77
|
+
"build": "(tsbuild)",
|
|
78
|
+
"buildDocs": "(tsdoc)"
|
|
79
|
+
}
|
|
80
|
+
}
|
package/readme.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# @fin.cx/opendata
|
|
2
2
|
|
|
3
|
-
`@fin.cx/opendata` is a TypeScript toolbox for
|
|
3
|
+
`@fin.cx/opendata` is a TypeScript toolbox for open business and market data: stock and crypto prices, SEC fundamentals, and lookups in the German registers through the register portal of the federal states. It is built for programmers who want scriptable APIs and real workflows instead of a thin wrapper around one remote endpoint.
|
|
4
4
|
|
|
5
|
-
It
|
|
5
|
+
It gives you three practical lanes in one package:
|
|
6
6
|
|
|
7
7
|
- `StockPriceService` for stock and crypto prices with caching, retries, failover, historical ranges, and intraday support
|
|
8
8
|
- `FundamentalsService` and `StockDataService` for SEC fundamentals and combined price + fundamentals payloads
|
|
9
|
-
- `
|
|
10
|
-
- `LawService` for syncing and searching German, EU, and US law texts in a local embedded database
|
|
9
|
+
- `HandelsregisterClient`, from `@fin.cx/opendata/handelsregister`, for single lookups in the commercial, cooperative, partnership, association and civil-law partnership registers over plain HTTP, without any storage
|
|
11
10
|
|
|
12
11
|
## Issue Reporting and Security
|
|
13
12
|
|
|
@@ -36,17 +35,18 @@ pnpm add @fin.cx/opendata
|
|
|
36
35
|
- `SecEdgarProvider`
|
|
37
36
|
Fundamentals provider backed by SEC EDGAR company facts.
|
|
38
37
|
|
|
39
|
-
### German
|
|
38
|
+
### German Registers
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
Starts a local embedded database, imports the public German company JSONL dump, and exposes a `handelsregister` helper for company lookup and file download.
|
|
40
|
+
Everything here comes from `@fin.cx/opendata/handelsregister`. That entry point imports nothing else of the package, needs no database, no browser and no download folder, and keeps nothing between calls.
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- `
|
|
47
|
-
|
|
48
|
-
- `
|
|
49
|
-
The
|
|
42
|
+
- `HandelsregisterClient`
|
|
43
|
+
Searches the register portal (handelsregister.de) by name or register number and retrieves one register sheet with its structured register content (SI) parsed into a typed result.
|
|
44
|
+
- `SlidingWindowGate`
|
|
45
|
+
Holds the portal's limit of 60 searches and retrievals per hour. The client asks it before every counted request.
|
|
46
|
+
- `parseStructuredContent()` and `parseSearchResults()`
|
|
47
|
+
The parsers on their own, for an SI file or a result page someone saved from the portal. They make no request.
|
|
48
|
+
- `registerCourts`, `findRegisterCourt()`, `parseRegisterReferenceText()`, `formatRegisterReference()`
|
|
49
|
+
The 150 register courts with their XJustiz codes, and helpers for register references as businesses print them (`Amtsgericht Bremen HRB 35230 HB`).
|
|
50
50
|
|
|
51
51
|
## Before You Start
|
|
52
52
|
|
|
@@ -54,10 +54,8 @@ pnpm add @fin.cx/opendata
|
|
|
54
54
|
- `MarketstackProvider` requires an API key.
|
|
55
55
|
- `SecEdgarProvider` requires a valid `User-Agent` string in the format `Company Name email@example.com`.
|
|
56
56
|
- `CoinGeckoProvider` works without an API key, but a key gives you better rate limits.
|
|
57
|
-
- `
|
|
58
|
-
-
|
|
59
|
-
- `LawService` stores data locally. It auto-starts on demand, but explicit `start()` and `stop()` keep lifecycle handling cleaner.
|
|
60
|
-
- EU law syncing and Handelsregister access use a headless browser under the hood.
|
|
57
|
+
- `HandelsregisterClient` needs a gate. The portal's terms allow at most 60 searches or retrievals of entities per hour from one installation, so share one `SlidingWindowGate` (or a gate of your own) among every client that reaches the portal from the same address.
|
|
58
|
+
- The portal's terms allow single lookups for information purposes. Systematic retrieval to build or update a copy of the registers is not allowed, and neither is publishing the data under the name "Handelsregister" (§ 8 Abs. 2 HGB).
|
|
61
59
|
- `StockDataService` defaults to a 24 hour price cache. If you want fresher quote caching, pass `cache.priceTTL` explicitly or use `StockPriceService` directly.
|
|
62
60
|
|
|
63
61
|
## Quick Start
|
|
@@ -194,118 +192,125 @@ console.log({
|
|
|
194
192
|
});
|
|
195
193
|
```
|
|
196
194
|
|
|
197
|
-
###
|
|
195
|
+
### Register Lookups (Handelsregister)
|
|
198
196
|
|
|
199
|
-
`
|
|
197
|
+
`HandelsregisterClient` speaks to the register portal of the federal states over plain HTTP, the way a browser would: it opens the search form, sends the search, reads the result list and, for one entry, downloads the structured register content (SI, an XJustiz XML file) and parses it.
|
|
200
198
|
|
|
201
199
|
```ts
|
|
202
|
-
import {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const company = await openData.handelsregister.getSpecificCompany({
|
|
218
|
-
court: 'Munich',
|
|
219
|
-
type: 'HRB',
|
|
220
|
-
number: '6684',
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
console.log({
|
|
224
|
-
searchHits: matches.length,
|
|
225
|
-
downloadedFiles: company.files.map((file) => file.path),
|
|
200
|
+
import {
|
|
201
|
+
HandelsregisterClient,
|
|
202
|
+
HandelsregisterError,
|
|
203
|
+
SlidingWindowGate,
|
|
204
|
+
findRegisterCourt,
|
|
205
|
+
formatRegisterReference,
|
|
206
|
+
} from '@fin.cx/opendata/handelsregister';
|
|
207
|
+
|
|
208
|
+
// one gate for the whole installation: 60 searches or retrievals per hour
|
|
209
|
+
const gate = new SlidingWindowGate();
|
|
210
|
+
|
|
211
|
+
const register = new HandelsregisterClient({
|
|
212
|
+
gate,
|
|
213
|
+
userAgent: 'Example Books (+https://example.com/contact)',
|
|
226
214
|
});
|
|
227
215
|
|
|
228
|
-
|
|
216
|
+
// by name: every row the portal lists, in its order, never narrowed down to one
|
|
217
|
+
const found = await register.search({ name: 'Task Venture Capital', seat: 'Bremen' });
|
|
218
|
+
const choices = found.matches.map((match) => ({
|
|
219
|
+
reference: formatRegisterReference(match.reference), // 'Amtsgericht Bremen HRB 35230 HB'
|
|
220
|
+
name: match.name,
|
|
221
|
+
sheet: match.sheetStatus, // 'current' or 'closed'
|
|
222
|
+
formerNames: match.formerNames,
|
|
223
|
+
}));
|
|
224
|
+
if (found.truncated) {
|
|
225
|
+
// the portal stops at 100 hits: add the seat, the court or the register number
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// one register sheet, with its structured content
|
|
229
|
+
const court = findRegisterCourt('Amtsgericht Bremen');
|
|
230
|
+
if (court) {
|
|
231
|
+
const entry = await register.getEntry({ courtCode: court.code, type: 'HRB', number: '35230 HB' });
|
|
232
|
+
const content = entry.structuredContent; // null when the portal offers none for this entry
|
|
233
|
+
if (content) {
|
|
234
|
+
const facts = {
|
|
235
|
+
name: content.name,
|
|
236
|
+
legalForm: content.legalForm?.short, // 'GmbH'
|
|
237
|
+
address: content.address,
|
|
238
|
+
status: content.status?.state, // 'active', 'deleted', 'in_liquidation', 'insolvent', 'merged'
|
|
239
|
+
representatives: content.representatives, // [{ name, function, functionCode, kind }]
|
|
240
|
+
retrievedAt: entry.retrievedAt,
|
|
241
|
+
notice: entry.notice.en, // the portal's note: the structured content is not binding
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// every failure is typed
|
|
247
|
+
try {
|
|
248
|
+
await register.getEntry({ courtCode: 'H1101', type: 'HRB', number: '35230' });
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (error instanceof HandelsregisterError && error.code === 'rate_limited') {
|
|
251
|
+
const nextPossibleAt = new Date(error.retryAt ?? Date.now());
|
|
252
|
+
}
|
|
253
|
+
}
|
|
229
254
|
```
|
|
230
255
|
|
|
231
|
-
|
|
256
|
+
Searches take a name or a register number:
|
|
232
257
|
|
|
233
|
-
- `
|
|
234
|
-
- `
|
|
235
|
-
- `await openData.handelsregister.searchCompany(name, limit)`
|
|
236
|
-
- `await openData.handelsregister.getSpecificCompany({ court, type, number })`
|
|
237
|
-
- `await openData.handelsregister.getSpecificCompanyByName(name)`
|
|
258
|
+
- `{ name, match?: 'all' | 'any' | 'exact', seat?, type?, courtCode?, includeClosed? }`
|
|
259
|
+
- `{ type, number, courtCode?, includeClosed? }` where `type` is `HRA`, `HRB`, `GnR`, `PR`, `VR` or `GsR` and `number` may carry the location suffix (`999924 B`, `999928 NM`). The portal is searched by the digits, so rows with any suffix come back. Without `courtCode`, every court's entry with that number is returned.
|
|
238
260
|
|
|
239
|
-
|
|
261
|
+
`getEntry({ courtCode, type, number })` identifies one sheet. It includes closed sheets, compares the location suffix when you give one, and never picks one of several: it fails with `not_found` or `ambiguous` and lists the candidates.
|
|
240
262
|
|
|
241
|
-
|
|
263
|
+
#### What counts against the 60 per hour
|
|
242
264
|
|
|
243
|
-
|
|
265
|
+
| Call | Counted requests | Also loaded |
|
|
266
|
+
| --- | --- | --- |
|
|
267
|
+
| `search()` | 1 search | the search form and the result list |
|
|
268
|
+
| `getEntry()` | 1 search, plus 1 retrieval when the portal offers structured content | the search form and the result list |
|
|
244
269
|
|
|
245
|
-
|
|
246
|
-
- EU via `EUR-Lex`
|
|
247
|
-
- US via GovInfo and Cornell LII
|
|
270
|
+
The client asks the gate before each counted request and before that step sends anything, so a refusal costs no traffic. A slot taken for a step that then fails before its request is sent stays taken; the count errs on the safe side. The client never repeats a request by itself, never caches, and never writes to the console. Every call opens its own visit to the portal and forgets it afterwards.
|
|
248
271
|
|
|
249
|
-
|
|
250
|
-
import { LawService } from '@fin.cx/opendata';
|
|
251
|
-
|
|
252
|
-
const laws = new LawService({
|
|
253
|
-
dbFolderPath: '.nogit/law-smartdb',
|
|
254
|
-
dbName: 'laws',
|
|
255
|
-
govInfoApiKey: process.env.GOVINFO_API_KEY,
|
|
256
|
-
});
|
|
272
|
+
A refusal of `SlidingWindowGate` is a `HandelsregisterError` with the code `rate_limited` and `retryAt`, the moment the next request is possible. Like every error of the client it carries the refused `step` (`search` or `structured_content`) and the `countedRequests` made before it: a `getEntry()` refused at the retrieval has already counted its search. A gate of your own may refuse with any error; a `HandelsregisterError` gains the step and the count the same way, and any other error reaches the caller unchanged.
|
|
257
273
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const germanLaw = await laws.syncLaw({
|
|
261
|
-
jurisdiction: 'de',
|
|
262
|
-
identifier: 'aeg',
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
const euLaw = await laws.syncLaw({
|
|
266
|
-
jurisdiction: 'eu',
|
|
267
|
-
identifier: '32024R1689',
|
|
268
|
-
language: 'EN',
|
|
269
|
-
});
|
|
274
|
+
#### What a result holds
|
|
270
275
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
276
|
+
- `IRegisterMatch`, one row of the result list: `reference` (court with code and name, register type, number with suffix), `formerCourt`, `federalState`, `name`, `seat`, `sheetStatus` (`current`, `closed` or `other`, with the portal's word in `sheetStatusText`), `formerNames` and the `documents` the portal offers.
|
|
277
|
+
- `IRegisterStructuredContent`, the parsed SI: `name`, `legalForm` (XUnternehmen code with short and long designation), `seat`, the business `address`, `register`, `status` (XJustiz status of the legal entity), `lastEntryOn`, `representatives` and `representationStated`, `producedAt`, `binding: false` and the portal's `notice`.
|
|
278
|
+
- A closed sheet ("Geschlossenes Registerblatt") means the entry was deleted, or its sheet was closed when the entity moved to another court, changed its legal form or merged. The result list does not say which, and the portal offers no structured content for it.
|
|
279
|
+
- The structured content states no date for the status itself; `lastEntryOn` is the date of the latest entry on the sheet.
|
|
280
|
+
- Of people the parser reads the name and the function only. Birth dates, places of residence and private addresses never leave it.
|
|
281
|
+
- Codes are decoded with tables taken from the code lists on XRepository on 2026-09-23. A code the tables do not know is kept as a code, without a designation.
|
|
275
282
|
|
|
276
|
-
|
|
277
|
-
jurisdiction: 'eu',
|
|
278
|
-
query: 'Artificial Intelligence Act',
|
|
279
|
-
limit: 5,
|
|
280
|
-
});
|
|
283
|
+
#### What can happen
|
|
281
284
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
285
|
+
| Case | Result |
|
|
286
|
+
| --- | --- |
|
|
287
|
+
| No match | `search()` returns no matches; `getEntry()` fails with `not_found` |
|
|
288
|
+
| Several matches in different courts | every row, in the portal's order |
|
|
289
|
+
| More than 100 hits | the first 100 and `truncated: true` |
|
|
290
|
+
| Renamed entity | the current name, earlier names in `formerNames` |
|
|
291
|
+
| Deleted or moved entity | `sheetStatus: 'closed'`, no structured content |
|
|
292
|
+
| Dissolved or insolvent entity | `status.state` `in_liquidation` or `insolvent`, and the liquidators among the representatives |
|
|
293
|
+
| Branch of a company under foreign law | its foreign legal form and seat, and its permanent representatives |
|
|
294
|
+
| Partnership, cooperative, association, civil-law partnership | register types `PR`, `GnR`, `VR`, `GsR` |
|
|
295
|
+
| Number without suffix that fits several sheets | `ambiguous` with the candidates |
|
|
296
|
+
| Suffix that fits no sheet | `not_found` with the sheets of the same number |
|
|
297
|
+
| Structured content of another sheet | `unexpected_response` |
|
|
298
|
+
| Portal answers with an HTTP error (blocked address, overload) | `refused` with `status` |
|
|
299
|
+
| Portal shows its notice that it refuses retrievals from this address | `refused` with its words in `portalMessage` |
|
|
300
|
+
| Portal too slow, unreachable, or call aborted | `timeout`, `unreachable`, `aborted` |
|
|
301
|
+
| Portal changed its pages or answered in another language | `unexpected_response`, with the portal's own message in `portalMessage` when it showed one |
|
|
302
|
+
| Structured content that cannot be read | `invalid_structured_content` |
|
|
288
303
|
|
|
289
|
-
|
|
290
|
-
```
|
|
304
|
+
Errors of the client carry `countedRequests` and the `step` that failed, so you can say whether a request may have counted even when its answer did not arrive. Error messages contain no register data.
|
|
291
305
|
|
|
292
|
-
|
|
306
|
+
#### Parsing without a request
|
|
293
307
|
|
|
294
308
|
```ts
|
|
295
|
-
|
|
296
|
-
jurisdiction: 'us',
|
|
297
|
-
usCollection: 'PLAW',
|
|
298
|
-
limit: 25,
|
|
299
|
-
since: new Date('2025-01-01'),
|
|
300
|
-
});
|
|
301
|
-
```
|
|
309
|
+
import { parseStructuredContent } from '@fin.cx/opendata/handelsregister';
|
|
302
310
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
- EU: `identifier: '32024R1689'`, `language: 'EN'`
|
|
307
|
-
- US public law package: `identifier: 'PLAW-119publ1'`, `usCollection: 'PLAW'`
|
|
308
|
-
- US Code citation: `identifier: '8 U.S.C. § 1226'`
|
|
311
|
+
// an SI file a person downloaded from the portal themselves
|
|
312
|
+
const content = parseStructuredContent(xmlText);
|
|
313
|
+
```
|
|
309
314
|
|
|
310
315
|
## API At A Glance
|
|
311
316
|
|
|
@@ -346,33 +351,61 @@ Useful request patterns:
|
|
|
346
351
|
- `getProviderStats()`
|
|
347
352
|
- `clearCache()`
|
|
348
353
|
|
|
349
|
-
### `
|
|
354
|
+
### `HandelsregisterClient` (`@fin.cx/opendata/handelsregister`)
|
|
355
|
+
|
|
356
|
+
- `new HandelsregisterClient({ gate, portalUrl?, userAgent?, requestTimeoutMs?, maxResponseBytes?, now? })`
|
|
357
|
+
- `search(query, { signal? })`
|
|
358
|
+
- `getEntry({ courtCode, type, number }, { signal? })`
|
|
359
|
+
|
|
360
|
+
### `SlidingWindowGate`
|
|
361
|
+
|
|
362
|
+
- `new SlidingWindowGate({ limit?, windowMs?, now? })`
|
|
363
|
+
- `acquire({ kind })`
|
|
364
|
+
- `remaining()`
|
|
365
|
+
- `nextAllowedAt()`
|
|
366
|
+
|
|
367
|
+
### Register helpers
|
|
368
|
+
|
|
369
|
+
- `parseStructuredContent(xml)`
|
|
370
|
+
- `parseSearchResults(html)`
|
|
371
|
+
- `registerCourts`
|
|
372
|
+
- `findRegisterCourt(codeOrName)`
|
|
373
|
+
- `parseRegisterReferenceText(text)`
|
|
374
|
+
- `parseRegisterNumber(text)`
|
|
375
|
+
- `formatRegisterReference(reference)`
|
|
376
|
+
- `structuredContentNotice`
|
|
377
|
+
|
|
378
|
+
## Data Sources
|
|
379
|
+
|
|
380
|
+
The package ships no market or register data. Prices and fundamentals come at run time from the providers you register (Marketstack, CoinGecko, SEC EDGAR), and register entries come at run time from the register portal of the federal states (handelsregister.de), one lookup at a time.
|
|
381
|
+
|
|
382
|
+
The only data the package carries are the code tables that `@fin.cx/opendata/handelsregister` uses to decode the structured register content (`ts/handelsregister/data.codelists.ts`). They are official code lists, published on XRepository (https://www.xrepository.de), and were taken on 2026-09-23:
|
|
383
|
+
|
|
384
|
+
| Table | Code list | Issued by |
|
|
385
|
+
| --- | --- | --- |
|
|
386
|
+
| `legalFormLabels` | `urn:xoev-de:xunternehmen:codeliste:rechtsformen_2.4` (Rechtsformen) | XUnternehmen |
|
|
387
|
+
| `roleLabels` | `urn:xoev-de:xjustiz:codeliste:gds.rollenbezeichnung_3.6` (GDS.Rollenbezeichnung) | BLK-AG IT-Standards in der Justiz |
|
|
388
|
+
| `entityStatusLabels` | `urn:xoev-de:xjustiz:codeliste:reg.status-rechtstraeger_2.0` (REG.Status_Rechtstraeger) | BLK-AG IT-Standards in der Justiz |
|
|
389
|
+
| `stateKeyToIso` | `urn:xoev-de:bund:bfj:codeliste:bfj.staat_7.0` (BfJ Staat) | Bundesamt für Justiz, Bonn |
|
|
390
|
+
| `registerCourts` | the register courts of the portal's search form, with their codes from `urn:xoev-de:xjustiz:codeliste:gds.gerichte` | BLK-AG IT-Standards in der Justiz |
|
|
350
391
|
|
|
351
|
-
|
|
352
|
-
- `stop()`
|
|
353
|
-
- `buildInitialDb()`
|
|
354
|
-
- `handelsregister.searchCompany(name, limit?)`
|
|
355
|
-
- `handelsregister.getSpecificCompany({ court, type, number })`
|
|
356
|
-
- `handelsregister.getSpecificCompanyByName(name)`
|
|
392
|
+
Codes and designations are reproduced as published, without changes. The tables hold a selection of each list, never an edited entry:
|
|
357
393
|
|
|
358
|
-
|
|
394
|
+
- `legalFormLabels`: the concrete legal forms that one of the justice registers can carry, with the short and the long designation;
|
|
395
|
+
- `roleLabels`: the 71 roles that occur in register content;
|
|
396
|
+
- `entityStatusLabels`: the whole list;
|
|
397
|
+
- `stateKeyToIso`: the key and the ISO 3166-1 alpha-2 code of each state or territory that has one;
|
|
398
|
+
- `registerCourts`: the 150 register courts as the portal's search form named them on the same day.
|
|
359
399
|
|
|
360
|
-
|
|
361
|
-
- `stop()`
|
|
362
|
-
- `getLaw(request)`
|
|
363
|
-
- `syncLaw(request)`
|
|
364
|
-
- `syncLaws(request)`
|
|
365
|
-
- `searchLaws({ query, jurisdiction?, limit? })`
|
|
400
|
+
A later version of a code list may add codes. The parser keeps a code it does not know as a code, without a designation, and never guesses one.
|
|
366
401
|
|
|
367
402
|
## Behavior And Caveats
|
|
368
403
|
|
|
369
404
|
- Historical market data is cached aggressively because it is treated as immutable.
|
|
370
405
|
- Intraday data uses interval-aware caching and tries incremental refreshes for recent data.
|
|
371
406
|
- `StockDataService` is convenience-first. `StockPriceService` is the better low-level choice if you care about request-shape-specific cache behavior.
|
|
372
|
-
-
|
|
373
|
-
-
|
|
374
|
-
- The most useful public `OpenData` features today are startup, JSONL import, and Handelsregister operations. Some extra validation/search helper methods exist in source but are still stubs.
|
|
375
|
-
- `LawService` persists synced laws locally, so repeated searches can stay offline once the data has been synced.
|
|
407
|
+
- The register lookup reads the portal's German pages and asks for German explicitly. It depends on the portal's current page structure: when that changes, calls end in `unexpected_response` rather than in guessed data.
|
|
408
|
+
- The structured register content is a non-binding service of the portal; the current printout is binding. Results carry `binding: false` and the portal's note.
|
|
376
409
|
- There is no hosted backend in this package. Everything is meant to run from your own TypeScript/Node process.
|
|
377
410
|
|
|
378
411
|
## Testing
|
|
@@ -381,7 +414,7 @@ Useful request patterns:
|
|
|
381
414
|
pnpm test
|
|
382
415
|
```
|
|
383
416
|
|
|
384
|
-
Some tests hit live remote services, so they may depend on network access
|
|
417
|
+
Some market data tests hit live remote services, so they may depend on network access or optional API credentials. The register lookup is tested against minimal hand-written pages and structured content with synthetic values only; the tests never contact the register portal.
|
|
385
418
|
|
|
386
419
|
## License and Legal Information
|
|
387
420
|
|