@rljson/edge 0.0.0 → 0.0.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.
package/README.de.md ADDED
@@ -0,0 +1,181 @@
1
+ <!--
2
+ @license
3
+ Copyright (c) 2025 Rljson
4
+
5
+ Use of this source code is governed by terms that can be
6
+ found in the LICENSE file in the root of this package.
7
+ -->
8
+
9
+ # @rljson/edge - Der Rljson-Beispieldatengenerator
10
+
11
+ Edge erzeugt Beispieldaten für Rljson-Anwendungen: eine Autowelt mit
12
+ Herstellern, ihren Fahrzeugkatalogen und für jedes Fahrzeug einem Preis,
13
+ einer Marke, einer Werkstatt, einer Stückliste und einer CAD-Szene. Eine
14
+ erzeugte Welt nutzt jeden Rljson-Datentyp, von hundert Zeilen für einen
15
+ Unit-Test bis zu 25 Millionen Zeilen für einen Lasttest.
16
+
17
+ ## Ziele
18
+
19
+ - Dem Uikit, `db`, den `io`-Backends und den Validatoren realistische
20
+ Testdaten in jedem Rljson-Datentyp liefern
21
+ - Bei jedem Lauf und auf jedem Rechner dieselben Zeilen und Hashes erzeugen,
22
+ ohne Zufallszahlen
23
+ - Vom Fixture eines Unit-Tests bis zum Lasttest mit 20 Millionen Objekten
24
+ skalieren
25
+ - In Node.js, im Browser und in Web Workern laufen, ohne Abhängigkeit zu
26
+ Dateisystem oder Datenbank
27
+ - Anschauliche Daten liefern: echte Marken und Modelle, plausible Teile,
28
+ Werkstätten mit Adresse und Inhaber
29
+
30
+ ## Stand
31
+
32
+ [![Tests](https://github.com/rljson/edge/actions/workflows/quick_check.yaml/badge.svg)](https://github.com/rljson/edge/actions/workflows/quick_check.yaml)
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pnpm add @rljson/edge @rljson/rljson
38
+ ```
39
+
40
+ ## Dokumentation
41
+
42
+ - [Beispieldatengenerator](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/topics/example-data-generator.md):
43
+ Entwurf, Größen und Ideen, im Projektmanagement-Repo
44
+ - [Entscheidungen edge-001 bis edge-004](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/decisions/000-index.md):
45
+ keine Buffets, kein Zufall, Callback-Sinks, abgeleitete Modelljahre
46
+ - Die Sektion »Generate Rljson« auf [rljson.github.io](https://rljson.github.io):
47
+ Schritt-für-Schritt-Tutorials
48
+ - [Guides](doc/guides): wie in diesem Repo entwickelt, getestet und
49
+ reviewt wird
50
+
51
+ ## Code-Beispiele
52
+
53
+ [src/example.ts](src/example.ts) führt diese Beispiele in den Tests aus.
54
+
55
+ Eine Welt im Speicher erzeugen:
56
+
57
+ ```ts
58
+ import { Edge } from '@rljson/edge';
59
+
60
+ // The tiny preset: one manufacturer with one catalog of five cars
61
+ const { world, stats } = await Edge.preset('tiny').generate();
62
+
63
+ console.log(`${stats.cars} cars, ${stats.rowsTotal} rows`);
64
+ console.log(world.manufacturers._data[0].name);
65
+ ```
66
+
67
+ Eine Welt konfigurieren und vorher ihre Größe schätzen:
68
+
69
+ ```ts
70
+ import { Edge } from '@rljson/edge';
71
+
72
+ const edge = new Edge({
73
+ manufacturers: { count: 2, catalogsPerManufacturer: 2 },
74
+ catalogs: { carsPerCatalog: 20, revisions: { count: 1 } },
75
+ layers: {
76
+ prices: { currencies: ['EUR', 'CHF'], range: { min: 20000, max: 80000 } },
77
+ brands: { modelsPerManufacturer: 3, popularity: 'zipf' },
78
+ workshops: { perCatalog: 4 },
79
+ parts: { depth: 3, fanOut: 2 },
80
+ cad: { depth: 2, fanOut: 3 },
81
+ },
82
+ });
83
+
84
+ console.log(`estimated rows: ${edge.estimate().rowsTotal}`);
85
+ const { stats } = await edge.generate();
86
+ console.log(`generated rows: ${stats.rowsTotal}`);
87
+ ```
88
+
89
+ Die Zeilen einer großen Welt über einen Callback weiterreichen, statt sie
90
+ zu behalten:
91
+
92
+ ```ts
93
+ import { Edge } from '@rljson/edge';
94
+
95
+ const rowsPerTable: Record<string, number> = {};
96
+ await Edge.preset('small').run({
97
+ onRow: (table) => {
98
+ rowsPerTable[table] = (rowsPerTable[table] ?? 0) + 1;
99
+ },
100
+ });
101
+ console.log(`prices: ${rowsPerTable.prices}, parts: ${rowsPerTable.parts}`);
102
+ ```
103
+
104
+ ## Funktionsweise
105
+
106
+ ### Die Welt
107
+
108
+ ```text
109
+ Manufacturers components manufacturers (der Einstieg)
110
+ └─ Manufacturer
111
+ ├─ Headquarters components addresses
112
+ └─ Catalogs jsonArray manufacturers.catalogsRef
113
+ └─ Catalog cakes catalogs (Slices sind Fahrzeuge)
114
+ ├─ Slice ids sliceIds carIds
115
+ ├─ Prices layers carPrices → components prices
116
+ ├─ Brands layers carBrands → components brands
117
+ ├─ Workshops layers carWorkshops → components workshops
118
+ │ ├─ addressRef → addresses
119
+ │ └─ ownerRef → persons
120
+ ├─ Parts layers carParts → components parts
121
+ │ └─ subPartRefs → parts
122
+ ├─ CAD layers carCad → trees cadScenes
123
+ │ └─ meta.partRef → parts
124
+ └─ Revisions revisions revisions (verknüpfen die Modelljahre)
125
+ ```
126
+
127
+ - Jedes Modelljahr eines Katalogs ist ein eigener Cake. Seine Slice-Ids und
128
+ Layer bauen mit `base`, `add` und `remove` auf dem Vorjahr auf, und eine
129
+ Revision verknüpft die beiden Cakes.
130
+ - Eine Stückliste reicht bis zu vier Ebenen unter das Fahrzeug. Die Ebenen
131
+ 1 bis 3 gehören zu einer Variante, Ebene 4 ist ein Pool von Normteilen,
132
+ den alle Stücklisten teilen. Eine CAD-Szene ist ein Baum aus Gruppen und
133
+ Meshes, und ein Mesh verweist auf ein Teil.
134
+ - Listen sind Hash-Arrays in einer `jsonArray`-Spalte, die die
135
+ Tabellenkonfiguration als Referenz deklariert. Jede Tabelle hat eine
136
+ `TableCfg`, so prüft `BaseValidator` aus `@rljson/rljson` Typen und
137
+ Referenzen.
138
+
139
+ ### Kein Zufall
140
+
141
+ Jeder Wert leitet sich aus einem Index in die Wörterbücher ab: 166
142
+ Hersteller, 917 Modelle, neun Baugruppen mit je 36 Teilen, 40 Normteile,
143
+ 126 Städte, 100 Straßen, 185 Vornamen und 180 Nachnamen. Ein Anteil wie
144
+ `discountShare` wählt jedes k-te Fahrzeug. Dieselbe Konfiguration ergibt
145
+ dieselben Hashes.
146
+
147
+ ### Zeile für Zeile
148
+
149
+ Zeilen verweisen per Hash auf Zeilen, deshalb gibt Edge eine Zeile erst
150
+ nach den Zeilen aus, auf die sie verweist. Edge hasht jede Zeile, verwirft
151
+ eine Zeile, deren Hash schon ausgegeben wurde, und reicht die übrigen
152
+ einzeln und abgewartet an einen Sink weiter. `generate()` sammelt sie in
153
+ einem `EMemorySink`; `run(sink)` reicht sie an `onRow` und behält
154
+ nichts, nachdem `onTable` jede Tabelle mit ihrer Konfiguration angekündigt
155
+ hat.
156
+
157
+ Stücklisten und Szenen werden pro Fahrzeug, pro Modell oder pro Katalog
158
+ geteilt. `estimate()` zählt die Zeilen jeder Tabelle allein aus der
159
+ Konfiguration, und `scale.targetRows` wählt damit die Fahrzeuge pro
160
+ Katalog.
161
+
162
+ ### Öffentliche API
163
+
164
+ | Export | Zweck |
165
+ | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
166
+ | `Edge` | `new Edge(config)`, `preset()`, `estimate()`, `generate()`, `run()` |
167
+ | `mergeConfigs` | Führt zwei Konfigurationen abschnittsweise zusammen |
168
+ | `edgeDefaults`, `resolveEdgeConfig` | Die Standardwerte und eine geprüfte Konfiguration mit Standardwerten |
169
+ | `edgePresets`, `edgePresetNames` | Die Presets `tiny`, `small`, `medium`, `large` und `xl` |
170
+ | `ESink`, `EMemorySink` | Die Callback-Schnittstelle von `run()` und der Sink von `generate()` |
171
+ | `carWorldTableKeys`, `carWorldTableCfgs` | Die Tabellenschlüssel und ihre Tabellenkonfigurationen |
172
+ | `ECarWorld` und seine Zeilentypen | `EManufacturer`, `EAddress`, `EPerson`, `EWorkshop`, `EPrice`, `EBrand`, `EPart`, `ECadMeta` |
173
+ | `EConfig` und seine Abschnitte | `ELayersConfig`, `EPricesConfig`, `EBrandsConfig`, `EWorkshopsConfig`, `EPartsConfig`, `ECadConfig`, `ERevisionsConfig`, `EResolvedConfig` |
174
+ | `EStats`, `EEstimate`, `EProgress` | Die Ergebnisse und der Fortschritt eines Laufs |
175
+ | Wörterbücher | `manufacturers`, `vehicles`, `modelsOf`, `assemblies`, `standardParts`, `materialsOf`, `cadSystems`, `cadDetails`, `cadMaterials`, `cities`, `citiesIn`, `cityNamed`, `streets`, `firstNames`, `lastNames` |
176
+
177
+ ## Mitwirken
178
+
179
+ Gearbeitet wird Ticket für Ticket mit `gg`, wie es der
180
+ [Develop Guide](doc/guides/develop-guide.md) beschreibt. Reviews folgen dem
181
+ [Review Guide](doc/guides/for-ai/ai-review-guide.md).
package/README.md CHANGED
@@ -8,4 +8,168 @@ found in the LICENSE file in the root of this package.
8
8
 
9
9
  # @rljson/edge - The Rljson Example Data Generator
10
10
 
11
- ## Users
11
+ Edge generates example data for Rljson applications: a car world with
12
+ manufacturers, their catalogs of cars, and for every car a price, a brand,
13
+ a workshop, a bill of materials and a CAD scene. One generated world uses
14
+ every Rljson data type, from a hundred rows for a unit test to 25 million
15
+ rows for a load test.
16
+
17
+ ## Goals
18
+
19
+ - Give the Uikit, `db`, the `io` backends and the validators realistic test
20
+ data in every Rljson data type
21
+ - Generate the same rows and hashes on every run and every machine, without
22
+ random numbers
23
+ - Scale from a unit test fixture to a load test with 20 million objects
24
+ - Run in Node.js, in the browser and in Web Workers, without file system or
25
+ database dependencies
26
+ - Keep the data vivid: real brands and models, plausible parts, workshops
27
+ with addresses and owners
28
+
29
+ ## State
30
+
31
+ [![Tests](https://github.com/rljson/edge/actions/workflows/quick_check.yaml/badge.svg)](https://github.com/rljson/edge/actions/workflows/quick_check.yaml)
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pnpm add @rljson/edge @rljson/rljson
37
+ ```
38
+
39
+ ## Documentation
40
+
41
+ - [Example data generator](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/topics/example-data-generator.md):
42
+ design, sizes and ideas, in the project management repo
43
+ - [Decisions edge-001 to edge-004](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/decisions/000-index.md):
44
+ no buffets, no randomness, callback sinks, derived model years
45
+ - The section »Generate Rljson« on [rljson.github.io](https://rljson.github.io):
46
+ step-by-step tutorials
47
+ - [Guides](doc/guides): how to develop, test and review in this repo
48
+
49
+ ## Code Examples
50
+
51
+ [src/example.ts](src/example.ts) runs these examples in the tests.
52
+
53
+ Generate a world in memory:
54
+
55
+ ```ts
56
+ import { Edge } from '@rljson/edge';
57
+
58
+ // The tiny preset: one manufacturer with one catalog of five cars
59
+ const { world, stats } = await Edge.preset('tiny').generate();
60
+
61
+ console.log(`${stats.cars} cars, ${stats.rowsTotal} rows`);
62
+ console.log(world.manufacturers._data[0].name);
63
+ ```
64
+
65
+ Configure a world and estimate its size first:
66
+
67
+ ```ts
68
+ import { Edge } from '@rljson/edge';
69
+
70
+ const edge = new Edge({
71
+ manufacturers: { count: 2, catalogsPerManufacturer: 2 },
72
+ catalogs: { carsPerCatalog: 20, revisions: { count: 1 } },
73
+ layers: {
74
+ prices: { currencies: ['EUR', 'CHF'], range: { min: 20000, max: 80000 } },
75
+ brands: { modelsPerManufacturer: 3, popularity: 'zipf' },
76
+ workshops: { perCatalog: 4 },
77
+ parts: { depth: 3, fanOut: 2 },
78
+ cad: { depth: 2, fanOut: 3 },
79
+ },
80
+ });
81
+
82
+ console.log(`estimated rows: ${edge.estimate().rowsTotal}`);
83
+ const { stats } = await edge.generate();
84
+ console.log(`generated rows: ${stats.rowsTotal}`);
85
+ ```
86
+
87
+ Stream the rows of a large world through a callback instead of keeping
88
+ them:
89
+
90
+ ```ts
91
+ import { Edge } from '@rljson/edge';
92
+
93
+ const rowsPerTable: Record<string, number> = {};
94
+ await Edge.preset('small').run({
95
+ onRow: (table) => {
96
+ rowsPerTable[table] = (rowsPerTable[table] ?? 0) + 1;
97
+ },
98
+ });
99
+ console.log(`prices: ${rowsPerTable.prices}, parts: ${rowsPerTable.parts}`);
100
+ ```
101
+
102
+ ## How It Works
103
+
104
+ ### The world
105
+
106
+ ```text
107
+ Manufacturers components manufacturers (the entry point)
108
+ └─ Manufacturer
109
+ ├─ Headquarters components addresses
110
+ └─ Catalogs jsonArray manufacturers.catalogsRef
111
+ └─ Catalog cakes catalogs (slices are cars)
112
+ ├─ Slice ids sliceIds carIds
113
+ ├─ Prices layers carPrices → components prices
114
+ ├─ Brands layers carBrands → components brands
115
+ ├─ Workshops layers carWorkshops → components workshops
116
+ │ ├─ addressRef → addresses
117
+ │ └─ ownerRef → persons
118
+ ├─ Parts layers carParts → components parts
119
+ │ └─ subPartRefs → parts
120
+ ├─ CAD layers carCad → trees cadScenes
121
+ │ └─ meta.partRef → parts
122
+ └─ Revisions revisions revisions (link the model years)
123
+ ```
124
+
125
+ - Every model year of a catalog is a cake of its own. Its slice ids and
126
+ layers build on the year before with `base`, `add` and `remove`, and a
127
+ revision links the two cakes.
128
+ - A bill of materials reaches up to four levels below the car. Levels 1 to
129
+ 3 belong to a variant, level 4 is a pool of standard parts every bill
130
+ shares. A CAD scene is a tree of groups and meshes, and a mesh refers to
131
+ a part.
132
+ - Lists are arrays of hashes in a `jsonArray` column that the table
133
+ configuration declares as a reference. Every table has a `TableCfg`, so
134
+ `BaseValidator` of `@rljson/rljson` checks types and references.
135
+
136
+ ### No randomness
137
+
138
+ Every value derives from an index into the dictionaries: 166
139
+ manufacturers, 917 models, nine assemblies with 36 parts each, 40
140
+ standard parts, 126 cities, 100 streets, 185 first names and 180 last
141
+ names. A share like `discountShare` selects every k-th car. The same
142
+ configuration yields the same hashes.
143
+
144
+ ### Row by row
145
+
146
+ Rows refer to rows by hash, so Edge emits a row after the rows it refers
147
+ to. It hashes every row, drops a row whose hash it emitted before, and
148
+ hands the rest to a sink, one at a time and awaited. `generate()` collects
149
+ them in an `EMemorySink`; `run(sink)` hands them to `onRow` and keeps
150
+ nothing, after `onTable` announced each table with its configuration.
151
+
152
+ Bills of materials and scenes are shared per car, per model or per
153
+ catalog. `estimate()` counts the rows of every table from the
154
+ configuration alone, and `scale.targetRows` uses it to pick the cars per
155
+ catalog.
156
+
157
+ ### Public API
158
+
159
+ | Export | Purpose |
160
+ | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
161
+ | `Edge` | `new Edge(config)`, `preset()`, `estimate()`, `generate()`, `run()` |
162
+ | `mergeConfigs` | Merges two configurations section by section |
163
+ | `edgeDefaults`, `resolveEdgeConfig` | The defaults, and a configuration with defaults and checks |
164
+ | `edgePresets`, `edgePresetNames` | The presets `tiny`, `small`, `medium`, `large` and `xl` |
165
+ | `ESink`, `EMemorySink` | The callback interface of `run()` and the sink of `generate()` |
166
+ | `carWorldTableKeys`, `carWorldTableCfgs` | The table keys and their table configurations |
167
+ | `ECarWorld` and its row types | `EManufacturer`, `EAddress`, `EPerson`, `EWorkshop`, `EPrice`, `EBrand`, `EPart`, `ECadMeta` |
168
+ | `EConfig` and its sections | `ELayersConfig`, `EPricesConfig`, `EBrandsConfig`, `EWorkshopsConfig`, `EPartsConfig`, `ECadConfig`, `ERevisionsConfig`, `EResolvedConfig` |
169
+ | `EStats`, `EEstimate`, `EProgress` | The results and the progress of a run |
170
+ | Dictionaries | `manufacturers`, `vehicles`, `modelsOf`, `assemblies`, `standardParts`, `materialsOf`, `cadSystems`, `cadDetails`, `cadMaterials`, `cities`, `citiesIn`, `cityNamed`, `streets`, `firstNames`, `lastNames` |
171
+
172
+ ## Contributing
173
+
174
+ Work ticket by ticket with `gg`, as the [Develop Guide](doc/guides/develop-guide.md)
175
+ describes. Reviews follow the [Review Guide](doc/guides/for-ai/ai-review-guide.md).
@@ -0,0 +1,181 @@
1
+ <!--
2
+ @license
3
+ Copyright (c) 2025 Rljson
4
+
5
+ Use of this source code is governed by terms that can be
6
+ found in the LICENSE file in the root of this package.
7
+ -->
8
+
9
+ # @rljson/edge - Der Rljson-Beispieldatengenerator
10
+
11
+ Edge erzeugt Beispieldaten für Rljson-Anwendungen: eine Autowelt mit
12
+ Herstellern, ihren Fahrzeugkatalogen und für jedes Fahrzeug einem Preis,
13
+ einer Marke, einer Werkstatt, einer Stückliste und einer CAD-Szene. Eine
14
+ erzeugte Welt nutzt jeden Rljson-Datentyp, von hundert Zeilen für einen
15
+ Unit-Test bis zu 25 Millionen Zeilen für einen Lasttest.
16
+
17
+ ## Ziele
18
+
19
+ - Dem Uikit, `db`, den `io`-Backends und den Validatoren realistische
20
+ Testdaten in jedem Rljson-Datentyp liefern
21
+ - Bei jedem Lauf und auf jedem Rechner dieselben Zeilen und Hashes erzeugen,
22
+ ohne Zufallszahlen
23
+ - Vom Fixture eines Unit-Tests bis zum Lasttest mit 20 Millionen Objekten
24
+ skalieren
25
+ - In Node.js, im Browser und in Web Workern laufen, ohne Abhängigkeit zu
26
+ Dateisystem oder Datenbank
27
+ - Anschauliche Daten liefern: echte Marken und Modelle, plausible Teile,
28
+ Werkstätten mit Adresse und Inhaber
29
+
30
+ ## Stand
31
+
32
+ [![Tests](https://github.com/rljson/edge/actions/workflows/quick_check.yaml/badge.svg)](https://github.com/rljson/edge/actions/workflows/quick_check.yaml)
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pnpm add @rljson/edge @rljson/rljson
38
+ ```
39
+
40
+ ## Dokumentation
41
+
42
+ - [Beispieldatengenerator](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/topics/example-data-generator.md):
43
+ Entwurf, Größen und Ideen, im Projektmanagement-Repo
44
+ - [Entscheidungen edge-001 bis edge-004](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/decisions/000-index.md):
45
+ keine Buffets, kein Zufall, Callback-Sinks, abgeleitete Modelljahre
46
+ - Die Sektion »Generate Rljson« auf [rljson.github.io](https://rljson.github.io):
47
+ Schritt-für-Schritt-Tutorials
48
+ - [Guides](doc/guides): wie in diesem Repo entwickelt, getestet und
49
+ reviewt wird
50
+
51
+ ## Code-Beispiele
52
+
53
+ [src/example.ts](src/example.ts) führt diese Beispiele in den Tests aus.
54
+
55
+ Eine Welt im Speicher erzeugen:
56
+
57
+ ```ts
58
+ import { Edge } from '@rljson/edge';
59
+
60
+ // The tiny preset: one manufacturer with one catalog of five cars
61
+ const { world, stats } = await Edge.preset('tiny').generate();
62
+
63
+ console.log(`${stats.cars} cars, ${stats.rowsTotal} rows`);
64
+ console.log(world.manufacturers._data[0].name);
65
+ ```
66
+
67
+ Eine Welt konfigurieren und vorher ihre Größe schätzen:
68
+
69
+ ```ts
70
+ import { Edge } from '@rljson/edge';
71
+
72
+ const edge = new Edge({
73
+ manufacturers: { count: 2, catalogsPerManufacturer: 2 },
74
+ catalogs: { carsPerCatalog: 20, revisions: { count: 1 } },
75
+ layers: {
76
+ prices: { currencies: ['EUR', 'CHF'], range: { min: 20000, max: 80000 } },
77
+ brands: { modelsPerManufacturer: 3, popularity: 'zipf' },
78
+ workshops: { perCatalog: 4 },
79
+ parts: { depth: 3, fanOut: 2 },
80
+ cad: { depth: 2, fanOut: 3 },
81
+ },
82
+ });
83
+
84
+ console.log(`estimated rows: ${edge.estimate().rowsTotal}`);
85
+ const { stats } = await edge.generate();
86
+ console.log(`generated rows: ${stats.rowsTotal}`);
87
+ ```
88
+
89
+ Die Zeilen einer großen Welt über einen Callback weiterreichen, statt sie
90
+ zu behalten:
91
+
92
+ ```ts
93
+ import { Edge } from '@rljson/edge';
94
+
95
+ const rowsPerTable: Record<string, number> = {};
96
+ await Edge.preset('small').run({
97
+ onRow: (table) => {
98
+ rowsPerTable[table] = (rowsPerTable[table] ?? 0) + 1;
99
+ },
100
+ });
101
+ console.log(`prices: ${rowsPerTable.prices}, parts: ${rowsPerTable.parts}`);
102
+ ```
103
+
104
+ ## Funktionsweise
105
+
106
+ ### Die Welt
107
+
108
+ ```text
109
+ Manufacturers components manufacturers (der Einstieg)
110
+ └─ Manufacturer
111
+ ├─ Headquarters components addresses
112
+ └─ Catalogs jsonArray manufacturers.catalogsRef
113
+ └─ Catalog cakes catalogs (Slices sind Fahrzeuge)
114
+ ├─ Slice ids sliceIds carIds
115
+ ├─ Prices layers carPrices → components prices
116
+ ├─ Brands layers carBrands → components brands
117
+ ├─ Workshops layers carWorkshops → components workshops
118
+ │ ├─ addressRef → addresses
119
+ │ └─ ownerRef → persons
120
+ ├─ Parts layers carParts → components parts
121
+ │ └─ subPartRefs → parts
122
+ ├─ CAD layers carCad → trees cadScenes
123
+ │ └─ meta.partRef → parts
124
+ └─ Revisions revisions revisions (verknüpfen die Modelljahre)
125
+ ```
126
+
127
+ - Jedes Modelljahr eines Katalogs ist ein eigener Cake. Seine Slice-Ids und
128
+ Layer bauen mit `base`, `add` und `remove` auf dem Vorjahr auf, und eine
129
+ Revision verknüpft die beiden Cakes.
130
+ - Eine Stückliste reicht bis zu vier Ebenen unter das Fahrzeug. Die Ebenen
131
+ 1 bis 3 gehören zu einer Variante, Ebene 4 ist ein Pool von Normteilen,
132
+ den alle Stücklisten teilen. Eine CAD-Szene ist ein Baum aus Gruppen und
133
+ Meshes, und ein Mesh verweist auf ein Teil.
134
+ - Listen sind Hash-Arrays in einer `jsonArray`-Spalte, die die
135
+ Tabellenkonfiguration als Referenz deklariert. Jede Tabelle hat eine
136
+ `TableCfg`, so prüft `BaseValidator` aus `@rljson/rljson` Typen und
137
+ Referenzen.
138
+
139
+ ### Kein Zufall
140
+
141
+ Jeder Wert leitet sich aus einem Index in die Wörterbücher ab: 166
142
+ Hersteller, 917 Modelle, neun Baugruppen mit je 36 Teilen, 40 Normteile,
143
+ 126 Städte, 100 Straßen, 185 Vornamen und 180 Nachnamen. Ein Anteil wie
144
+ `discountShare` wählt jedes k-te Fahrzeug. Dieselbe Konfiguration ergibt
145
+ dieselben Hashes.
146
+
147
+ ### Zeile für Zeile
148
+
149
+ Zeilen verweisen per Hash auf Zeilen, deshalb gibt Edge eine Zeile erst
150
+ nach den Zeilen aus, auf die sie verweist. Edge hasht jede Zeile, verwirft
151
+ eine Zeile, deren Hash schon ausgegeben wurde, und reicht die übrigen
152
+ einzeln und abgewartet an einen Sink weiter. `generate()` sammelt sie in
153
+ einem `EMemorySink`; `run(sink)` reicht sie an `onRow` und behält
154
+ nichts, nachdem `onTable` jede Tabelle mit ihrer Konfiguration angekündigt
155
+ hat.
156
+
157
+ Stücklisten und Szenen werden pro Fahrzeug, pro Modell oder pro Katalog
158
+ geteilt. `estimate()` zählt die Zeilen jeder Tabelle allein aus der
159
+ Konfiguration, und `scale.targetRows` wählt damit die Fahrzeuge pro
160
+ Katalog.
161
+
162
+ ### Öffentliche API
163
+
164
+ | Export | Zweck |
165
+ | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
166
+ | `Edge` | `new Edge(config)`, `preset()`, `estimate()`, `generate()`, `run()` |
167
+ | `mergeConfigs` | Führt zwei Konfigurationen abschnittsweise zusammen |
168
+ | `edgeDefaults`, `resolveEdgeConfig` | Die Standardwerte und eine geprüfte Konfiguration mit Standardwerten |
169
+ | `edgePresets`, `edgePresetNames` | Die Presets `tiny`, `small`, `medium`, `large` und `xl` |
170
+ | `ESink`, `EMemorySink` | Die Callback-Schnittstelle von `run()` und der Sink von `generate()` |
171
+ | `carWorldTableKeys`, `carWorldTableCfgs` | Die Tabellenschlüssel und ihre Tabellenkonfigurationen |
172
+ | `ECarWorld` und seine Zeilentypen | `EManufacturer`, `EAddress`, `EPerson`, `EWorkshop`, `EPrice`, `EBrand`, `EPart`, `ECadMeta` |
173
+ | `EConfig` und seine Abschnitte | `ELayersConfig`, `EPricesConfig`, `EBrandsConfig`, `EWorkshopsConfig`, `EPartsConfig`, `ECadConfig`, `ERevisionsConfig`, `EResolvedConfig` |
174
+ | `EStats`, `EEstimate`, `EProgress` | Die Ergebnisse und der Fortschritt eines Laufs |
175
+ | Wörterbücher | `manufacturers`, `vehicles`, `modelsOf`, `assemblies`, `standardParts`, `materialsOf`, `cadSystems`, `cadDetails`, `cadMaterials`, `cities`, `citiesIn`, `cityNamed`, `streets`, `firstNames`, `lastNames` |
176
+
177
+ ## Mitwirken
178
+
179
+ Gearbeitet wird Ticket für Ticket mit `gg`, wie es der
180
+ [Develop Guide](doc/guides/develop-guide.md) beschreibt. Reviews folgen dem
181
+ [Review Guide](doc/guides/for-ai/ai-review-guide.md).