@fin.cx/opendata 4.8.0 → 4.9.1

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 (44) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/gleif/classes.gleifclient.d.ts +26 -0
  3. package/dist_ts/gleif/classes.gleifclient.js +214 -0
  4. package/dist_ts/gleif/classes.gleiferror.d.ts +6 -0
  5. package/dist_ts/gleif/classes.gleiferror.js +11 -0
  6. package/dist_ts/gleif/crosswalk.d.ts +7 -0
  7. package/dist_ts/gleif/crosswalk.js +25 -0
  8. package/dist_ts/gleif/csv.d.ts +2 -0
  9. package/dist_ts/gleif/csv.js +75 -0
  10. package/dist_ts/gleif/data.crosswalk.d.ts +12 -0
  11. package/dist_ts/gleif/data.crosswalk.js +1265 -0
  12. package/dist_ts/gleif/index.d.ts +6 -0
  13. package/dist_ts/gleif/index.js +7 -0
  14. package/dist_ts/gleif/interfaces.d.ts +101 -0
  15. package/dist_ts/gleif/interfaces.js +2 -0
  16. package/dist_ts/gleif/plugins.d.ts +1 -0
  17. package/dist_ts/gleif/plugins.js +3 -0
  18. package/dist_ts/gleif/records.d.ts +6 -0
  19. package/dist_ts/gleif/records.js +107 -0
  20. package/dist_ts/gleif/stream.zip.d.ts +2 -0
  21. package/dist_ts/gleif/stream.zip.js +54 -0
  22. package/dist_ts/handelsregister/data.codelists.d.ts +2 -0
  23. package/dist_ts/handelsregister/data.codelists.js +10 -10
  24. package/dist_ts/handelsregister/interfaces.d.ts +5 -1
  25. package/dist_ts/handelsregister/parse.structuredcontent.js +6 -4
  26. package/dist_ts/plugins.d.ts +2 -0
  27. package/dist_ts/plugins.js +4 -1
  28. package/package.json +7 -2
  29. package/readme.md +79 -4
  30. package/ts/00_commitinfo_data.ts +1 -1
  31. package/ts/gleif/classes.gleifclient.ts +170 -0
  32. package/ts/gleif/classes.gleiferror.ts +8 -0
  33. package/ts/gleif/crosswalk.ts +29 -0
  34. package/ts/gleif/csv.ts +60 -0
  35. package/ts/gleif/data.crosswalk.ts +1271 -0
  36. package/ts/gleif/index.ts +6 -0
  37. package/ts/gleif/interfaces.ts +107 -0
  38. package/ts/gleif/plugins.ts +2 -0
  39. package/ts/gleif/records.ts +104 -0
  40. package/ts/gleif/stream.zip.ts +43 -0
  41. package/ts/handelsregister/data.codelists.ts +11 -9
  42. package/ts/handelsregister/interfaces.ts +5 -1
  43. package/ts/handelsregister/parse.structuredcontent.ts +5 -3
  44. package/ts/plugins.ts +4 -0
@@ -0,0 +1,6 @@
1
+ export * from './interfaces.js';
2
+ export { GleifClient, gleifDeltaKinds, gleifGoldenCopyBaseUrl, gleifRestBaseUrl } from './classes.gleifclient.js';
3
+ export { GleifError, type TGleifErrorCode } from './classes.gleiferror.js';
4
+ export { parseGleifRestRecord } from './records.js';
5
+ export { courtFromRegistrationAuthority, registrationAuthorityFromCourt, parseRegistrationAuthorityEntityId, registerReferenceFromRegistrationAuthority } from './crosswalk.js';
6
+ export { germanRegistrationAuthorities, gleifUnmappedCourts, gleifRegistrationAuthoritiesSource } from './data.crosswalk.js';
@@ -0,0 +1,107 @@
1
+ import type { TRegisterType } from '../handelsregister/interfaces.js';
2
+
3
+ export interface IGleifName {
4
+ name: string;
5
+ language?: string;
6
+ /** The published other-name type; absent for the legal name. */
7
+ type?: string;
8
+ }
9
+
10
+ export interface IGleifAddress {
11
+ addressLines: string[];
12
+ city: string;
13
+ country: string;
14
+ language?: string;
15
+ region?: string;
16
+ postalCode?: string;
17
+ addressNumber?: string;
18
+ addressNumberWithinBuilding?: string;
19
+ mailRouting?: string;
20
+ }
21
+
22
+ /** Level 1 reference data, with source strings and ISO 8601 dates preserved. No persistence. */
23
+ export interface IGleifRecord {
24
+ lei: string;
25
+ legalName: IGleifName;
26
+ otherNames: IGleifName[];
27
+ transliteratedOtherNames: IGleifName[];
28
+ legalForm?: { elfCode?: string; other?: string };
29
+ legalAddress: IGleifAddress;
30
+ headquartersAddress: IGleifAddress;
31
+ jurisdiction?: string;
32
+ entityStatus: string;
33
+ entityCategory?: string;
34
+ registrationAuthority?: { id?: string; otherId?: string; entityId?: string };
35
+ registrationStatus: string;
36
+ initialRegistrationDate: string;
37
+ lastUpdateDate: string;
38
+ }
39
+
40
+ export type TGleifDelta = 'IntraDay' | 'LastDay' | 'LastWeek' | 'LastMonth';
41
+ export type TGleifFileKind = 'GoldenCopy' | TGleifDelta;
42
+
43
+ /** The API's LEI-CDF 3.1 ZIP/CSV descriptor. `size` is compressed bytes. */
44
+ export interface IGleifGoldenCopyFile {
45
+ url: string;
46
+ format: 'csv';
47
+ cdfVersion: 'LEI_3.1';
48
+ kind: TGleifFileKind;
49
+ recordCount: number;
50
+ size: number;
51
+ }
52
+
53
+ export interface IGleifPublish {
54
+ /** UTC publication label exactly as returned, `YYYY-MM-DD HH:mm:ss`. */
55
+ publishDate: string;
56
+ fullFile: IGleifGoldenCopyFile;
57
+ deltaFiles: Record<TGleifDelta, IGleifGoldenCopyFile>;
58
+ }
59
+
60
+ export type TGleifFetch = (input: string, init: RequestInit) => Promise<Response>;
61
+
62
+ export interface IGleifClientOptions {
63
+ fetch?: TGleifFetch;
64
+ /** Default: https://goldencopy.gleif.org/api/v2 */
65
+ goldenCopyBaseUrl?: string;
66
+ /** Default: https://api.gleif.org/api/v1 */
67
+ restBaseUrl?: string;
68
+ userAgent?: string;
69
+ /** Per request, including streaming and consumer time; default 30 minutes. */
70
+ timeoutMs?: number;
71
+ }
72
+
73
+ export interface IGleifReadOptions {
74
+ /** ISO 3166-1 alpha-2; compares the legal address's country, not the headquarters. */
75
+ country?: string;
76
+ /** Exact legal jurisdiction, e.g. DE or US-DE. Both filters, if given, must match. */
77
+ jurisdiction?: string;
78
+ signal?: AbortSignal;
79
+ /** Maximum CSV row size in Unicode code points, including the header; default 1,048,576. */
80
+ maxRowChars?: number;
81
+ }
82
+
83
+ export interface IGleifStreamOptions extends IGleifReadOptions {
84
+ /** Default GoldenCopy; deltas overlap and are current records, not event histories. */
85
+ kind?: TGleifFileKind;
86
+ }
87
+
88
+ export interface IGleifRegisterNumber {
89
+ type: TRegisterType;
90
+ number: string;
91
+ }
92
+
93
+ export interface IGleifRegistrationAuthority {
94
+ raCode: string;
95
+ name: string;
96
+ jurisdiction: string;
97
+ registerName: string;
98
+ courtCode?: string;
99
+ /** Present exactly when this German RA has no court in opendata's list. */
100
+ unmappedReason?: string;
101
+ }
102
+
103
+ export interface IGleifUnmappedCourt {
104
+ courtCode: string;
105
+ name: string;
106
+ reason: string;
107
+ }
@@ -0,0 +1,2 @@
1
+ // External dependencies for this subpath only; no portal client or storage imports.
2
+ export { fflate } from '../plugins.js';
@@ -0,0 +1,104 @@
1
+ import { GleifError } from './classes.gleiferror.js';
2
+ import type { IGleifAddress, IGleifName, IGleifRecord } from './interfaces.js';
3
+
4
+ const bad = (labelArg: string): never => { throw new GleifError('unexpected_answer', `Missing or invalid GLEIF ${labelArg}.`); };
5
+ const object = (valueArg: unknown, labelArg: string): Record<string, unknown> =>
6
+ valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Record<string, unknown> : bad(labelArg);
7
+ const optional = (valueArg: unknown): string | undefined => {
8
+ if (valueArg === undefined || valueArg === null || valueArg === '') return undefined;
9
+ return typeof valueArg === 'string' ? valueArg : bad('string');
10
+ };
11
+ const required = (valueArg: unknown, labelArg: string): string => optional(valueArg) ?? bad(labelArg);
12
+ const date = (valueArg: unknown, labelArg: string): string => {
13
+ const value = required(valueArg, labelArg);
14
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value) || !Number.isFinite(Date.parse(value))) bad(labelArg);
15
+ return value;
16
+ };
17
+ const compact = <TArg extends object>(valueArg: TArg): TArg => Object.fromEntries(Object.entries(valueArg).filter(([, valueArg]) => valueArg !== undefined)) as TArg;
18
+ const name = (valueArg: unknown): IGleifName => {
19
+ const value = object(valueArg, 'name');
20
+ return compact({ name: required(value.name, 'name'), language: optional(value.language), type: optional(value.type) });
21
+ };
22
+ const names = (valueArg: unknown): IGleifName[] => {
23
+ if (valueArg === undefined || valueArg === null) return [];
24
+ if (!Array.isArray(valueArg)) bad('names');
25
+ return (valueArg as unknown[]).map(name);
26
+ };
27
+ const address = (valueArg: unknown): IGleifAddress => {
28
+ const value = object(valueArg, 'address');
29
+ if (!Array.isArray(value.addressLines) || value.addressLines.length < 1) bad('address lines');
30
+ return compact({
31
+ addressLines: (value.addressLines as unknown[]).map((lineArg) => required(lineArg, 'address line')),
32
+ city: required(value.city, 'city'), country: required(value.country, 'country'),
33
+ language: optional(value.language), region: optional(value.region), postalCode: optional(value.postalCode),
34
+ addressNumber: optional(value.addressNumber), addressNumberWithinBuilding: optional(value.addressNumberWithinBuilding), mailRouting: optional(value.mailRouting),
35
+ });
36
+ };
37
+
38
+ /** Maps the documented JSON:API attributes, not the differently shaped Golden Copy JSON. */
39
+ export const parseGleifRestRecord = (valueArg: unknown): IGleifRecord => {
40
+ const data = object(object(valueArg, 'response').data, 'data');
41
+ if (data.type !== 'lei-records') bad('resource type');
42
+ const attributes = object(data.attributes, 'attributes');
43
+ const entity = object(attributes.entity, 'entity');
44
+ const registration = object(attributes.registration, 'registration');
45
+ const lei = required(attributes.lei, 'LEI');
46
+ if (!/^[A-Z0-9]{18}\d{2}$/.test(lei) || data.id !== lei) bad('LEI');
47
+ const legalForm = entity.legalForm === undefined || entity.legalForm === null ? undefined : object(entity.legalForm, 'legal form');
48
+ const registeredAt = entity.registeredAt === undefined || entity.registeredAt === null ? undefined : object(entity.registeredAt, 'registration authority');
49
+ const form = legalForm ? compact({ elfCode: optional(legalForm.id), other: optional(legalForm.other) }) : undefined;
50
+ const authority = compact({ id: optional(registeredAt?.id), otherId: optional(registeredAt?.other), entityId: optional(entity.registeredAs) });
51
+ return compact({
52
+ lei, legalName: name(entity.legalName), otherNames: names(entity.otherNames), transliteratedOtherNames: names(entity.transliteratedOtherNames),
53
+ legalForm: form && Object.keys(form).length ? form : undefined,
54
+ legalAddress: address(entity.legalAddress), headquartersAddress: address(entity.headquartersAddress),
55
+ jurisdiction: optional(entity.jurisdiction), entityStatus: required(entity.status, 'entity status'), entityCategory: optional(entity.category),
56
+ registrationAuthority: Object.keys(authority).length ? authority : undefined,
57
+ registrationStatus: required(registration.status, 'registration status'),
58
+ initialRegistrationDate: date(registration.initialRegistrationDate, 'initial registration date'), lastUpdateDate: date(registration.lastUpdateDate, 'last update date'),
59
+ });
60
+ };
61
+
62
+ /** Column names verified against the published LEI-CDF 3.1 CSV (the optional values may be empty). */
63
+ export const gleifCsvColumns = [
64
+ 'LEI', 'Entity.LegalName', 'Entity.LegalName.xmllang', 'Entity.LegalJurisdiction', 'Entity.EntityStatus', 'Entity.EntityCategory',
65
+ 'Entity.LegalForm.EntityLegalFormCode', 'Entity.LegalForm.OtherLegalForm',
66
+ 'Entity.RegistrationAuthority.RegistrationAuthorityID', 'Entity.RegistrationAuthority.OtherRegistrationAuthorityID', 'Entity.RegistrationAuthority.RegistrationAuthorityEntityID',
67
+ 'Registration.RegistrationStatus', 'Registration.InitialRegistrationDate', 'Registration.LastUpdateDate',
68
+ ...['Entity.LegalAddress', 'Entity.HeadquartersAddress'].flatMap((prefixArg) => ['FirstAddressLine', 'City', 'Country'].map((fieldArg) => `${prefixArg}.${fieldArg}`)),
69
+ ] as const;
70
+
71
+ export const csvRecordMapper = (headerArg: string[]): ((rowArg: string[]) => IGleifRecord) => {
72
+ if (new Set(headerArg).size !== headerArg.length || gleifCsvColumns.some((columnArg) => !headerArg.includes(columnArg))) {
73
+ throw new GleifError('invalid_csv', 'Missing or duplicate LEI-CDF 3.1 columns.');
74
+ }
75
+ const indices = new Map(headerArg.map((columnArg, indexArg) => [columnArg, indexArg]));
76
+ const nameColumns = (prefixArg: string): string[] => headerArg.filter((columnArg) => new RegExp(`^${prefixArg.replaceAll('.', '\\.') }\\.\\d+$`).test(columnArg));
77
+ const otherColumns = nameColumns('Entity.OtherEntityNames.OtherEntityName');
78
+ const transliteratedColumns = nameColumns('Entity.TransliteratedOtherEntityNames.TransliteratedOtherEntityName');
79
+ return (rowArg) => {
80
+ if (rowArg.length !== headerArg.length) throw new GleifError('invalid_csv', 'LEI-CDF CSV row has a different column count from its header.');
81
+ const get = (columnArg: string): string | undefined => {
82
+ const index = indices.get(columnArg);
83
+ return index === undefined ? undefined : rowArg[index];
84
+ };
85
+ const nameOf = (prefixArg: string) => ({ name: get(prefixArg), language: get(`${prefixArg}.xmllang`), type: get(`${prefixArg}.type`) });
86
+ const addressOf = (prefixArg: string) => ({
87
+ addressLines: [get(`${prefixArg}.FirstAddressLine`), ...[1, 2, 3].map((indexArg) => get(`${prefixArg}.AdditionalAddressLine.${indexArg}`))].filter(Boolean),
88
+ city: get(`${prefixArg}.City`), country: get(`${prefixArg}.Country`), language: get(`${prefixArg}.xmllang`), region: get(`${prefixArg}.Region`), postalCode: get(`${prefixArg}.PostalCode`),
89
+ addressNumber: get(`${prefixArg}.AddressNumber`), addressNumberWithinBuilding: get(`${prefixArg}.AddressNumberWithinBuilding`), mailRouting: get(`${prefixArg}.MailRouting`),
90
+ });
91
+ const lei = get('LEI');
92
+ return parseGleifRestRecord({ data: { type: 'lei-records', id: lei, attributes: { lei, entity: {
93
+ legalName: nameOf('Entity.LegalName'), otherNames: otherColumns.filter((columnArg) => get(columnArg)).map(nameOf),
94
+ transliteratedOtherNames: transliteratedColumns.filter((columnArg) => get(columnArg)).map(nameOf),
95
+ legalForm: { id: get('Entity.LegalForm.EntityLegalFormCode'), other: get('Entity.LegalForm.OtherLegalForm') },
96
+ legalAddress: addressOf('Entity.LegalAddress'), headquartersAddress: addressOf('Entity.HeadquartersAddress'),
97
+ jurisdiction: get('Entity.LegalJurisdiction'), status: get('Entity.EntityStatus'), category: get('Entity.EntityCategory'),
98
+ registeredAt: { id: get('Entity.RegistrationAuthority.RegistrationAuthorityID'), other: get('Entity.RegistrationAuthority.OtherRegistrationAuthorityID') },
99
+ registeredAs: get('Entity.RegistrationAuthority.RegistrationAuthorityEntityID'),
100
+ }, registration: {
101
+ status: get('Registration.RegistrationStatus'), initialRegistrationDate: get('Registration.InitialRegistrationDate'), lastUpdateDate: get('Registration.LastUpdateDate'),
102
+ } } } });
103
+ };
104
+ };
@@ -0,0 +1,43 @@
1
+ import * as plugins from './plugins.js';
2
+ import { GleifError } from './classes.gleiferror.js';
3
+
4
+ /** One CSV member, incrementally inflated. Input slices bound each synchronous output batch. */
5
+ export async function* unzipCsv(sourceArg: AsyncIterable<Uint8Array>): AsyncGenerator<Uint8Array> {
6
+ let member: plugins.fflate.UnzipFile | undefined;
7
+ let complete = false;
8
+ let failure: Error | undefined;
9
+ let output: Uint8Array[] = [];
10
+ const unzipper = new plugins.fflate.Unzip((fileArg) => {
11
+ if (member || !fileArg.name.toLowerCase().endsWith('.csv')) {
12
+ throw new GleifError('invalid_archive', 'Expected exactly one CSV member in the Golden Copy ZIP.');
13
+ }
14
+ member = fileArg;
15
+ fileArg.ondata = (errorArg, dataArg, finalArg) => {
16
+ if (errorArg) failure = errorArg;
17
+ else if (dataArg.length) output.push(dataArg);
18
+ if (finalArg) complete = true;
19
+ };
20
+ fileArg.start();
21
+ });
22
+ unzipper.register(plugins.fflate.UnzipInflate);
23
+ try {
24
+ for await (const chunk of sourceArg) {
25
+ for (let offset = 0; offset < chunk.length; offset += 1024) {
26
+ unzipper.push(chunk.subarray(offset, offset + 1024));
27
+ if (failure) throw failure;
28
+ const ready = output;
29
+ output = [];
30
+ for (const data of ready) yield data;
31
+ }
32
+ }
33
+ unzipper.push(new Uint8Array(), true);
34
+ if (failure) throw failure;
35
+ for (const data of output) yield data;
36
+ if (!member || !complete) throw new GleifError('invalid_archive', 'Missing or truncated CSV member in Golden Copy ZIP.');
37
+ } catch (error) {
38
+ if (error instanceof GleifError) throw error;
39
+ throw new GleifError('invalid_archive', 'Could not stream the Golden Copy ZIP.', undefined, { cause: error });
40
+ } finally {
41
+ member?.terminate();
42
+ }
43
+ }
@@ -17,6 +17,8 @@ export interface ILegalFormLabel {
17
17
  short?: string;
18
18
  /** full designation, for example `Gesellschaft mit beschränkter Haftung` */
19
19
  long: string;
20
+ /** economic activity of a natural person, in XUnternehmen category 400000; not a public result field */
21
+ naturalPerson?: true;
20
22
  }
21
23
 
22
24
  /** XUnternehmen legal form code → designation */
@@ -106,15 +108,15 @@ export const legalFormLabels: Readonly<Record<string, ILegalFormLabel>> = {
106
108
  '294000': { short: 'ausl. juristische Person (EU-Recht)', long: 'ausländische juristische Person (EU-Recht)' },
107
109
  '295000': { short: 'ausl. juristische Person (Nicht-EU-Recht)', long: 'ausländische juristische Person (Nicht-EU-Recht)' },
108
110
  '298100': { short: 'sonst. ausl. juristische Person des privaten Rechts (Auffangtatb. Steuer)', long: 'sonstige ausländische juristische Person des privaten Rechts (Auffangtatbestand Steuer)' },
109
- '410000': { long: 'gewerbliches Einzelunternehmen (ohne Hausgewerbe)' },
110
- '411000': { short: 'e.K.; e.Kfm.; e.Kfr.', long: 'eingetragenes gewerbliches Einzelunternehmen (e.K.; e.Kfm.; e.Kfr.)' },
111
- '411100': { short: 'e.K.; e.Kfm.; e.Kfr. (Sitz in DE)', long: 'eingetragenes gewerbliches Einzelunternehmen (e.K.; e.Kfm.; e.Kfr.) (Sitz in Deutschland)' },
112
- '411200': { short: 'e.K.; e.Kfm.; e.Kfr. (Sitz im Ausland)', long: 'eingetragenes gewerbliches Einzelunternehmen (e.K.; e.Kfm.; e.Kfr.) (Sitz im Ausland)' },
113
- '422000': { short: 'Land-/Forstwirt', long: 'Land-/Forstwirt' },
114
- '428100': { short: 'sonst. wirtsch. Tätigkeit einer nat. Person', long: 'sonstige wirtschaftliche Tätigkeit einer natürlichen Person (Auffangtatbestand)' },
115
- '490000': { short: 'ausl. wirtsch. Tätigkeit einer nat. Person', long: 'ausländische wirtschaftliche Tätigkeit einer natürlichen Person' },
116
- '491000': { short: 'ausl. gew. Einzelunternehmen (EU-Recht)', long: 'ausländische gewerbliches Einzelunternehmen (EU-Recht)' },
117
- '492000': { short: 'ausl. gew. Einzelunternehmen (Nicht-EU-Recht)', long: 'ausländisches gewerbliches Einzelunternehmen (Nicht-EU-Recht)' },
111
+ '410000': { long: 'gewerbliches Einzelunternehmen (ohne Hausgewerbe)', naturalPerson: true },
112
+ '411000': { short: 'e.K.; e.Kfm.; e.Kfr.', long: 'eingetragenes gewerbliches Einzelunternehmen (e.K.; e.Kfm.; e.Kfr.)', naturalPerson: true },
113
+ '411100': { short: 'e.K.; e.Kfm.; e.Kfr. (Sitz in DE)', long: 'eingetragenes gewerbliches Einzelunternehmen (e.K.; e.Kfm.; e.Kfr.) (Sitz in Deutschland)', naturalPerson: true },
114
+ '411200': { short: 'e.K.; e.Kfm.; e.Kfr. (Sitz im Ausland)', long: 'eingetragenes gewerbliches Einzelunternehmen (e.K.; e.Kfm.; e.Kfr.) (Sitz im Ausland)', naturalPerson: true },
115
+ '422000': { short: 'Land-/Forstwirt', long: 'Land-/Forstwirt', naturalPerson: true },
116
+ '428100': { short: 'sonst. wirtsch. Tätigkeit einer nat. Person', long: 'sonstige wirtschaftliche Tätigkeit einer natürlichen Person (Auffangtatbestand)', naturalPerson: true },
117
+ '490000': { short: 'ausl. wirtsch. Tätigkeit einer nat. Person', long: 'ausländische wirtschaftliche Tätigkeit einer natürlichen Person', naturalPerson: true },
118
+ '491000': { short: 'ausl. gew. Einzelunternehmen (EU-Recht)', long: 'ausländische gewerbliches Einzelunternehmen (EU-Recht)', naturalPerson: true },
119
+ '492000': { short: 'ausl. gew. Einzelunternehmen (Nicht-EU-Recht)', long: 'ausländisches gewerbliches Einzelunternehmen (Nicht-EU-Recht)', naturalPerson: true },
118
120
  '540000': { short: 'Gewerbebetrieb einer KöR', long: 'Gewerbebetrieb einer Körperschaft des öffentlichen Rechts' },
119
121
  '581000': { long: 'sonstige wirtschaftliche Tätigkeit einer nicht-natürlichen Person (Auffangtatbestand)' },
120
122
  '681000': { short: 'Freitext', long: 'Freitext (Auffangtatbestand)' },
@@ -178,7 +178,11 @@ export interface IRegisterContentRegister {
178
178
 
179
179
  /** the parsed structured register content (SI) of one entry */
180
180
  export interface IRegisterStructuredContent {
181
- /** whether the content describes the entity as an organisation or as a natural person */
181
+ /**
182
+ * The registered entity: natural-person business legal forms mean person, even in an XJustiz
183
+ * organisation element. Otherwise an organisation holder means organisation, its absence person.
184
+ * Unknown/missing legal forms retain that structural rule; representatives do not decide it.
185
+ */
182
186
  entityKind: 'organisation' | 'person';
183
187
  name: string;
184
188
  legalForm?: IRegisterLegalForm;
@@ -120,7 +120,7 @@ const readLegalForm = (organisationsArg: TXmlElement[]): IRegisterLegalForm | un
120
120
  const code = codeAt(organisation, 'angabenZurRechtsform', 'rechtsform');
121
121
  if (code) {
122
122
  const label = legalFormLabels[code];
123
- return label ? { code, ...label } : { code };
123
+ return label ? { code, ...(label.short ? { short: label.short } : {}), long: label.long } : { code };
124
124
  }
125
125
  }
126
126
  return undefined;
@@ -313,8 +313,11 @@ export const parseStructuredContent = (xmlArg: string): IRegisterStructuredConte
313
313
  }
314
314
 
315
315
  const holders = entityRole.participant.kind === 'person' ? [...organisations, entityRole.participant.element] : organisations;
316
+ const legalForm = readLegalForm(organisations);
316
317
  const content: IRegisterStructuredContent = {
317
- entityKind: organisations.length > 0 ? 'organisation' : 'person',
318
+ // XJustiz also represents natural-person businesses as organisations. Unknown/missing forms
319
+ // retain the structural rule: an organisation holder means organisation, otherwise person.
320
+ entityKind: legalForm && legalFormLabels[legalForm.code]?.naturalPerson ? 'person' : organisations.length > 0 ? 'organisation' : 'person',
318
321
  name,
319
322
  register: readRegister(root, organisations),
320
323
  representationStated: entitled.length > 0,
@@ -322,7 +325,6 @@ export const parseStructuredContent = (xmlArg: string): IRegisterStructuredConte
322
325
  binding: false,
323
326
  notice: structuredContentNotice,
324
327
  };
325
- const legalForm = readLegalForm(organisations);
326
328
  const seat = readSeat(organisations);
327
329
  const address = readAddress(holders);
328
330
  const status = readStatus(baseData);
package/ts/plugins.ts CHANGED
@@ -24,3 +24,7 @@ export {
24
24
  smartpromise,
25
25
  smartrequest,
26
26
  }
27
+
28
+ // third-party scope
29
+ import * as fflate from 'fflate';
30
+ export { fflate };